<commit_before>package docker

import (
	"errors"
	"fmt"
	"io"
	"math/rand"
	"os"
	"strings"
	"time"

	"golang.org/x/net/context"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/iron-io/titan/runner/drivers"
	"github.com/iron-io/titan/runner/drivers/common"
)

type DockerDriver struct {
	conf     *common.Config
	docker   *docker.Client
	hostname string
	rand     *rand.Rand
}

func NewDocker(conf *common.Config) (*DockerDriver, error) {
	hostname, err := os.Hostname()
	if err != nil {
		log.WithError(err).Fatal("couldn't resolve hostname")
	}

	// docker, err := docker.NewClient(conf.Docker)
	docker, err := docker.NewClientFromEnv()
	if err != nil {
		return nil, err
	}

	return &DockerDriver{
		conf:     conf,
		docker:   docker,
		hostname: hostname,
		rand:     rand.New(rand.NewSource(time.Now().Unix())),
	}, nil
}

// Run executes the docker container. If task runs, drivers.RunResult will be returned. If something fails outside the task (ie: Docker), it will return error.
// todo: pass in context
func (drv *DockerDriver) Run(ctx context.Context, task drivers.ContainerTask) (drivers.RunResult, error) {
	container, err := drv.startTask(task)
	if container != "" {
		// It is possible that startTask created a container but could not start it. So always try to remove a valid container.
		defer drv.removeContainer(container)
	}
	if err != nil {
		return nil, err
	}

	sentence := make(chan string, 1)

	go drv.nanny(ctx, container, task, sentence)

	log := task.Logger()
	if log == nil {
		return nil, fmt.Errorf("Received nil logger")
	}

	w := &limitedWriter{W: log, N: 8 * 1024 * 1024 * 1024} // TODO get max log size from somewhere

	// Docker sometimes fails to close the attach response connection even after
	// the container stops, leaving the runner stuck. We use a non-blocking
	// attach so we can sleep a bit after WaitContainer returns and then forcibly
	// close the connection.
	closer, err := drv.docker.AttachToContainerNonBlocking(docker.AttachToContainerOptions{
		Container: container, OutputStream: w, ErrorStream: w,
		Stream: true, Logs: true, Stdout: true, Stderr: true})
	defer closer.Close()
	if err != nil {
		return nil, err
	}

	// It's possible the execution could be finished here, then what? http://docs.docker.com.s3-website-us-east-1.amazonaws.com/engine/reference/api/docker_remote_api_v1.20/#wait-a-container
	exitCode, err := drv.docker.WaitContainer(container)
	time.Sleep(10 * time.Millisecond)
	if err != nil {
		return nil, err
	}

	status, err := drv.status(exitCode, sentence)
	// the err returned above is an error from running user code, so we don't return it from this method.
	return &runResult{
		StatusValue: status,
		Err:         err,
	}, nil
}

func (drv *DockerDriver) error(err error) *runResult {
	return &runResult{
		Err:         err,
		StatusValue: drivers.StatusError,
	}
}

func (drv *DockerDriver) startTask(task drivers.ContainerTask) (dockerId string, err error) {
	cID, err := drv.createContainer(task)
	if err != nil {
		return "", err
	}

	err = drv.docker.StartContainer(cID, nil)
	return cID, err
}

func writeFile(name, body string) error {
	f, err := os.Create(name)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(f, strings.NewReader(body))
	return err
}

func (drv *DockerDriver) createContainer(task drivers.ContainerTask) (string, error) {
	l := log.WithFields(log.Fields{
		"image": task.Image(),
		// todo: add context fields here, job id, etc.
	})

	if task.Image() == "" {
		return "", errors.New("no image specified, this runner cannot run this")
	}

	var cmd []string
	if task.Command() != "" {
		// TODO: maybe check for spaces or shell meta characters?
		// There's a possibility that the container doesn't have sh.
		cmd = []string{"sh", "-c", task.Command()}
	}

	envvars := make([]string, 0, len(task.EnvVars())+4)
	for name, val := range task.EnvVars() {
		envvars = append(envvars, name+"="+val)
	}

	container := docker.CreateContainerOptions{
		Config: &docker.Config{
			Env:       envvars,
			Cmd:       cmd,
			Memory:    int64(drv.conf.Memory),
			CPUShares: drv.conf.CPUShares,
			Hostname:  drv.hostname,
			Image:     task.Image(),
			Volumes:   map[string]struct{}{},
		},
		HostConfig: &docker.HostConfig{},
	}

	volumes := task.Volumes()
	container.HostConfig.Binds = make([]string, len(volumes))
	for i, mapping := range volumes {
		if len(mapping) != 2 {
			return "", fmt.Errorf("Invalid volume tuple %d. Tuple must be 2-element", i)
		}

		hostDir := mapping[0]
		containerDir := mapping[1]
		container.Config.Volumes[containerDir] = struct{}{}
		container.HostConfig.Binds[i] = fmt.Sprintf("%s:%s", hostDir, containerDir)

		if i == 0 {
			container.Config.WorkingDir = containerDir
		}
	}

	c, err := drv.docker.CreateContainer(container)
	if err != nil {
		if err != docker.ErrNoSuchImage {
			return "", err
		}
		l.WithError(err).Infoln("could not create container, trying to pull...")

		regHost := "docker.io"
		repo, tag := docker.ParseRepositoryTag(task.Image())
		split := strings.Split(repo, "/")
		if len(split) >= 3 {
			// then we have an explicit registry
			regHost = split[0]
		}
		// todo: we should probably move all this auth stuff up a level, don't need to do it for every job
		authConfig := docker.AuthConfiguration{}
		auth := task.Auth()
		if auth != "" {
			l.Debugln("Using auth", auth)
			read := strings.NewReader(fmt.Sprintf(`{"%s":{"auth":"%s"}}`, regHost, auth))
			ac, err := docker.NewAuthConfigurations(read)
			if err != nil {
				return "", err
			}
			authConfig = ac.Configs[regHost]
		}

		err = drv.docker.PullImage(docker.PullImageOptions{Repository: repo, Tag: tag}, authConfig)
		if err != nil {
			return "", err
		}

		// should have it now
		c, err = drv.docker.CreateContainer(container)
		if err != nil {
			return "", err
		}
	}
	return c.ID, nil
}

func (drv *DockerDriver) removeContainer(container string) {
	// TODO: trap error
	drv.docker.RemoveContainer(docker.RemoveContainerOptions{
		ID: container, Force: true, RemoveVolumes: true})
}

// watch for cancel or timeout and kill process.
func (drv *DockerDriver) nanny(ctx context.Context, container string, task drivers.ContainerTask, sentence chan<- string) {
	select {
	case <-ctx.Done():
		switch ctx.Err() {
		case context.DeadlineExceeded:
			sentence <- drivers.StatusTimeout
			drv.cancel(container)
		case context.Canceled:
			sentence <- drivers.StatusKilled
			drv.cancel(container)
		}
	}
}

func (drv *DockerDriver) status(exitCode int, sentence <-chan string) (string, error) {
	var status string
	var err error
	select {
	case status = <-sentence: // use this if killed / timed out
	default:
		switch exitCode {
		case 0:
			status = drivers.StatusSuccess
		case 137:
			// Probably an OOM kill

			// TODO: try harder to detect OOM kills. We can call
			// docker.InspectContainer and look at
			// container.State.OOMKilled, but this field isn't set
			// consistently.
			// See: https://github.com/docker/docker/issues/15621

			status = drivers.StatusKilled
			// TODO: better message; show memory limit
			err = drivers.ErrOutOfMemory
		default:
			status = drivers.StatusError
			err = fmt.Errorf("exit code %d", exitCode)
		}
	}
	return status, err
}

// TODO we _sure_ it's dead?
func (drv *DockerDriver) cancel(container string) { drv.docker.StopContainer(container, 5) }
<commit_msg>plumb version, review comments<commit_after>package docker

import (
	"errors"
	"fmt"
	"io"
	"math/rand"
	"os"
	"strings"
	"time"

	"golang.org/x/net/context"

	log "github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/iron-io/titan/runner/drivers"
	"github.com/iron-io/titan/runner/drivers/common"
)

type DockerDriver struct {
	conf     *common.Config
	docker   *docker.Client
	hostname string
	rand     *rand.Rand
}

func NewDocker(conf *common.Config) (*DockerDriver, error) {
	hostname, err := os.Hostname()
	if err != nil {
		log.WithError(err).Fatal("couldn't resolve hostname")
	}

	// docker, err := docker.NewClient(conf.Docker)
	docker, err := docker.NewClientFromEnv()
	if err != nil {
		return nil, err
	}

	return &DockerDriver{
		conf:     conf,
		docker:   docker,
		hostname: hostname,
		rand:     rand.New(rand.NewSource(time.Now().Unix())),
	}, nil
}

// Run executes the docker container. If task runs, drivers.RunResult will be returned. If something fails outside the task (ie: Docker), it will return error.
// todo: pass in context
func (drv *DockerDriver) Run(ctx context.Context, task drivers.ContainerTask) (drivers.RunResult, error) {
	container, err := drv.startTask(task)
	if container != "" {
		// It is possible that startTask created a container but could not start it. So always try to remove a valid container.
		defer drv.removeContainer(container)
	}
	if err != nil {
		return nil, err
	}

	sentence := make(chan string, 1)

	go drv.nanny(ctx, container, task, sentence)

	log := task.Logger()
	if log == nil {
		return nil, fmt.Errorf("Received nil logger")
	}

	w := &limitedWriter{W: log, N: 8 * 1024 * 1024 * 1024} // TODO get max log size from somewhere

	// Docker sometimes fails to close the attach response connection even after
	// the container stops, leaving the runner stuck. We use a non-blocking
	// attach so we can sleep a bit after WaitContainer returns and then forcibly
	// close the connection.
	closer, err := drv.docker.AttachToContainerNonBlocking(docker.AttachToContainerOptions{
		Container: container, OutputStream: w, ErrorStream: w,
		Stream: true, Logs: true, Stdout: true, Stderr: true})
	defer closer.Close()
	if err != nil {
		return nil, err
	}

	// It's possible the execution could be finished here, then what? http://docs.docker.com.s3-website-us-east-1.amazonaws.com/engine/reference/api/docker_remote_api_v1.20/#wait-a-container
	exitCode, err := drv.docker.WaitContainer(container)
	time.Sleep(10 * time.Millisecond)
	if err != nil {
		return nil, err
	}

	status, err := drv.status(exitCode, sentence)
	// the err returned above is an error from running user code, so we don't return it from this method.
	return &runResult{
		StatusValue: status,
		Err:         err,
	}, nil
}

func (drv *DockerDriver) error(err error) *runResult {
	return &runResult{
		Err:         err,
		StatusValue: drivers.StatusError,
	}
}

func (drv *DockerDriver) startTask(task drivers.ContainerTask) (dockerId string, err error) {
	cID, err := drv.createContainer(task)
	if err != nil {
		return "", err
	}

	err = drv.docker.StartContainer(cID, nil)
	return cID, err
}

func writeFile(name, body string) error {
	f, err := os.Create(name)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.Copy(f, strings.NewReader(body))
	return err
}

func (drv *DockerDriver) createContainer(task drivers.ContainerTask) (string, error) {
	l := log.WithFields(log.Fields{
		"image": task.Image(),
		// todo: add context fields here, job id, etc.
	})

	if task.Image() == "" {
		return "", errors.New("no image specified, this runner cannot run this")
	}

	var cmd []string
	if task.Command() != "" {
		// TODO: maybe check for spaces or shell meta characters?
		// There's a possibility that the container doesn't have sh.
		cmd = []string{"sh", "-c", task.Command()}
	}

	envvars := make([]string, 0, len(task.EnvVars())+4)
	for name, val := range task.EnvVars() {
		envvars = append(envvars, name+"="+val)
	}

	container := docker.CreateContainerOptions{
		Config: &docker.Config{
			Env:       envvars,
			Cmd:       cmd,
			Memory:    int64(drv.conf.Memory),
			CPUShares: drv.conf.CPUShares,
			Hostname:  drv.hostname,
			Image:     task.Image(),
			Volumes:   map[string]struct{}{},
		},
		HostConfig: &docker.HostConfig{},
	}

	volumes := task.Volumes()
	container.HostConfig.Binds = make([]string, len(volumes))
	for i, mapping := range volumes {
		if len(mapping) != 2 {
			return "", fmt.Errorf("Invalid volume tuple: %v. Tuple must be 2-element", mapping)
		}

		hostDir := mapping[0]
		containerDir := mapping[1]
		container.Config.Volumes[containerDir] = struct{}{}
		container.HostConfig.Binds[i] = fmt.Sprintf("%s:%s", hostDir, containerDir)

		if i == 0 {
			container.Config.WorkingDir = containerDir
		}
	}

	c, err := drv.docker.CreateContainer(container)
	if err != nil {
		if err != docker.ErrNoSuchImage {
			return "", err
		}
		l.WithError(err).Infoln("could not create container, trying to pull...")

		regHost := "docker.io"
		repo, tag := docker.ParseRepositoryTag(task.Image())
		split := strings.Split(repo, "/")
		if len(split) >= 3 {
			// then we have an explicit registry
			regHost = split[0]
		}
		// todo: we should probably move all this auth stuff up a level, don't need to do it for every job
		authConfig := docker.AuthConfiguration{}
		auth := task.Auth()
		if auth != "" {
			l.Debugln("Using auth", auth)
			read := strings.NewReader(fmt.Sprintf(`{"%s":{"auth":"%s"}}`, regHost, auth))
			ac, err := docker.NewAuthConfigurations(read)
			if err != nil {
				return "", err
			}
			authConfig = ac.Configs[regHost]
		}

		err = drv.docker.PullImage(docker.PullImageOptions{Repository: repo, Tag: tag}, authConfig)
		if err != nil {
			return "", err
		}

		// should have it now
		c, err = drv.docker.CreateContainer(container)
		if err != nil {
			return "", err
		}
	}
	return c.ID, nil
}

func (drv *DockerDriver) removeContainer(container string) {
	// TODO: trap error
	drv.docker.RemoveContainer(docker.RemoveContainerOptions{
		ID: container, Force: true, RemoveVolumes: true})
}

// watch for cancel or timeout and kill process.
func (drv *DockerDriver) nanny(ctx context.Context, container string, task drivers.ContainerTask, sentence chan<- string) {
	select {
	case <-ctx.Done():
		switch ctx.Err() {
		case context.DeadlineExceeded:
			sentence <- drivers.StatusTimeout
			drv.cancel(container)
		case context.Canceled:
			sentence <- drivers.StatusKilled
			drv.cancel(container)
		}
	}
}

func (drv *DockerDriver) status(exitCode int, sentence <-chan string) (string, error) {
	var status string
	var err error
	select {
	case status = <-sentence: // use this if killed / timed out
	default:
		switch exitCode {
		case 0:
			status = drivers.StatusSuccess
		case 137:
			// Probably an OOM kill

			// TODO: try harder to detect OOM kills. We can call
			// docker.InspectContainer and look at
			// container.State.OOMKilled, but this field isn't set
			// consistently.
			// See: https://github.com/docker/docker/issues/15621

			status = drivers.StatusKilled
			// TODO: better message; show memory limit
			err = drivers.ErrOutOfMemory
		default:
			status = drivers.StatusError
			err = fmt.Errorf("exit code %d", exitCode)
		}
	}
	return status, err
}

// TODO we _sure_ it's dead?
func (drv *DockerDriver) cancel(container string) { drv.docker.StopContainer(container, 5) }
<|endoftext|>


<commit_before>package db

import (
	"time"
)

//User is the model for an entity which has access to the API
type User struct {
	ID       int       `db:"id" json:"id"`
	Created  time.Time `db:"created" json:"created"`
	Username string    `db:"username" json:"username" binding:"required"`
	Name     string    `db:"name" json:"name" binding:"required"`
	Email    string    `db:"email" json:"email" binding:"required"`
	Password string    `db:"password" json:"-"` // password hash
	Admin    bool      `db:"admin" json:"admin"`
	External bool      `db:"external" json:"external"`
	Alert    bool      `db:"alert" json:"alert"`
}

// UserWithPwd extends User structure with field for unhashed password received from JSON.
type UserWithPwd struct {
	Pwd string `db:"-" json:"password"` // unhashed password from JSON
	User
}

func ValidateUser(user User) error {
	return nil
}
<commit_msg>fix(be): validate user data<commit_after>package db

import (
	"time"
)

// User is the model for an entity which has access to the API
type User struct {
	ID       int       `db:"id" json:"id"`
	Created  time.Time `db:"created" json:"created"`
	Username string    `db:"username" json:"username" binding:"required"`
	Name     string    `db:"name" json:"name" binding:"required"`
	Email    string    `db:"email" json:"email" binding:"required"`
	Password string    `db:"password" json:"-"` // password hash
	Admin    bool      `db:"admin" json:"admin"`
	External bool      `db:"external" json:"external"`
	Alert    bool      `db:"alert" json:"alert"`
}

// UserWithPwd extends User structure with field for unhashed password received from JSON.
type UserWithPwd struct {
	Pwd string `db:"-" json:"password"` // unhashed password from JSON
	User
}

func ValidateUser(user User) error {
	if user.Username == "" {
		return &ValidationError{Message: "Username cannot be empty"}
	}
	if user.Email == "" {
		return &ValidationError{Message: "Email cannot be empty"}
	}
	if user.Name == "" {
		return &ValidationError{Message: "Name cannot be empty"}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	ui "github.com/gizak/termui"
	"strings"
	"time"
)

func switchToSelectedPodcastScreen(configuration *Configuration) {
	podcasts := getCurrentPagePodcasts()
	if currentSelected >= len(podcasts) || currentSelected < 0 {
		return
	}
	currentSelectedPodcast = podcasts[currentSelected]
	currentScreen = PodcastDetail
}

func searchPodcastsFromTui(configuration *Configuration) {
	// search TODO use go()
	currentSelected = 0
	var err error
	searchString := strings.Replace(userTextBuffer, "\n", "", -1)
	searchString = strings.Trim(searchString, "\n\t")
	searchString = strings.Replace(searchString, " ", "+", -1) //replace spaces with plus to not break everything
	currentPodcastsInBuffers[currentScreen], err = searchItunes(searchString)
	if err != nil {
		userTextBuffer = "error searching! " + err.Error()
	}
}

func enterPressedHome(configuration *Configuration) {
	// TODO refactor into two functions
	if currentMode == Normal {
		switchToSelectedPodcastScreen(configuration)
	}
}

func enterPressedSearch(configuration *Configuration) {
	if currentMode == Normal {
		switchToSelectedPodcastScreen(configuration)
	} else {
		searchPodcastsFromTui(configuration)
	}
}

func enterPressedDownloaded(configuration *Configuration) {
	podcasts := getCurrentPagePodcastEpisodes()
	if currentSelected >= len(podcasts) || currentSelected < 0 {
		return
	}
	SetPlaying(podcasts[currentSelected].StorageLocation)
	SetPlayerState(Play)
}

func enterPressedPodcastDetail(configuration *Configuration) {
	podcasts := getCurrentPagePodcastEpisodes()
	if currentSelected >= len(podcasts) || currentSelected < 0 {
		return
	}
	if podcastIsDownloaded(configuration, podcasts[currentSelected]) {
		return
	}
	// TODO fix this race condition/bad configuration management.
	go func() {
		configuration, err := downloadPodcast(configuration, currentSelectedPodcast, podcasts[currentSelected])
		if err == nil {
			writeConfig(configuration)
		}
	}()
}

func escapePressedHome(configuration *Configuration) {

}

func escapePressedSearch(configuration *Configuration) {

}

func escapePressedDownloaded(configuration *Configuration) {

}

func actionPressedHome(configuration *Configuration) {

}

func actionPressedSearch(configuration *Configuration) {
	subscribedKey := -1
	podcasts := getCurrentPagePodcasts()
	if currentSelected >= len(podcasts) || currentSelected < 0 {
		return
	}
	selectedPodcast := podcasts[currentSelected]
	// check if it's already part of the configuration
	for key, value := range configuration.Subscribed {
		if selectedPodcast.ArtistName == value.ArtistName && selectedPodcast.CollectionName == value.CollectionName {
			subscribedKey = key
		}
	}
	if subscribedKey != -1 {
		configuration.Subscribed = append(configuration.Subscribed[:subscribedKey], configuration.Subscribed[subscribedKey+1:]...)
	} else {
		configuration.Subscribed = append(configuration.Subscribed, selectedPodcast) //now subscribe by adding it to the subscribed list
	}
	writeConfig(configuration)
}

func actionPressedDownloaded(configuration *Configuration) {

}

func actionPressedPodcastDetail(configuration *Configuration) {

}

func upPressedHome(configuration *Configuration) {
	if currentSelected > 0 {
		currentSelected--
	}
}

func upPressedSearch(configuration *Configuration) {
	if currentSelected > 0 {
		currentSelected--
	} else {
		currentMode = Insert
	}
}

func upPressedDownloaded(configuration *Configuration) {
	if currentSelected > 0 {
		currentSelected--
	}
}

func upPressedPodcastDetail(configuration *Configuration) {
	if currentSelected > 0 {
		currentSelected--
	}
}

func downPressedHome(configuration *Configuration) {
	if currentSelected < currentListSize-1 {
		currentSelected++
	}
}

func downPressedSearch(configuration *Configuration) {
	if currentMode == Insert && len(userTextBuffer) > 0 {
		searchPodcastsFromTui(configuration)
	} else if currentSelected < currentListSize-1 {
		currentSelected++
	}
}

func downPressedDownloaded(configuration *Configuration) {
	if currentSelected < currentListSize-1 {
		currentSelected++
	}
}

func downPressedPodcastDetail(configuration *Configuration) {
	if currentSelected < currentListSize-1 {
		currentSelected++
	}
}

func searchPressedHome(configuration *Configuration) {
	userTextBuffer = ""
	currentMode = Insert
}

func searchPressedSearch(configuration *Configuration) {
	userTextBuffer = ""
	currentMode = Insert
}

func searchPressedDownloaded(configuration *Configuration) {
	userTextBuffer = ""
	currentMode = Insert
}

func handleEventsGlobal(configuration *Configuration, event ui.Event) bool {
	if event.ID == "<Enter>" {
		enterPressed[currentScreen](configuration)
		// reset mode on enter after the action is done
		currentMode = Normal
	} else if event.ID == "<Escape>" {
		// reset mode on Escape as well as possibly do an action
		currentMode = Normal
		escapePressed[currentScreen](configuration)
	} else if (event.ID == configuration.LeftKeybind && currentMode == Normal) || event.ID == "<Left>" {
		transitionScreen(leftTransitions, currentScreen)
	} else if (event.ID == configuration.RightKeybind && currentMode == Normal) || event.ID == "<Right>" {
		transitionScreen(rightTransitions, currentScreen)
	} else if (event.ID == configuration.UpKeybind && currentMode == Normal) || event.ID == "<Up>" {
		upPressed[currentScreen](configuration)
	} else if (event.ID == configuration.DownKeybind && currentMode == Normal) || event.ID == "<Down>" {
		downPressed[currentScreen](configuration)
		currentMode = Normal
	} else {
		// nothing matches, return false
		return false
	}
	// matches, return true
	return true
}

func handleKeyboard(configuration *Configuration, event ui.Event) {
	if handleEventsGlobal(configuration, event) {
		// handled
	} else if currentMode == Insert {
		if event.ID == "<Backspace>" || event.ID == "<Delete>" || event.ID == "C-8>" {
			if len(userTextBuffer) > 0 {
				userTextBuffer = userTextBuffer[:len(userTextBuffer)-1]
			}
		} else if event.ID == "<Space>" {
			userTextBuffer += " "
		} else if event.ID == "<Tab>" {
			userTextBuffer += "	"
		} else if len(event.ID) > 0 && string([]rune(event.ID)[0]) == "<" {
			// do not do anything if it's one of the other control keys
		} else {
			userTextBuffer += event.ID
		}
	} else if event.ID == configuration.PlayKeybind {
		TogglePlayerState()
	} else if event.ID == configuration.SearchKeybind {
		searchPressed[currentScreen](configuration)
	} else if event.ID == configuration.ActionKeybind {
		actionPressed[currentScreen](configuration)
	}
}

func handleMouse(configuration *Configuration, event ui.Event) {
	if event.ID == "<MouseWheelUp>" {
		upPressed[currentScreen](configuration)
	} else if event.ID == "<MouseWheelDown>" {
		downPressed[currentScreen](configuration)
	} else if event.ID == "<MouseLeft>" {
		enterPressed[currentScreen](configuration)
	} else if event.ID == "<MouseRight>" && currentScreen == PodcastDetail {
		transitionScreen(leftTransitions, currentScreen)
	}
}

func tuiMainLoop(configuration *Configuration) {
	width := ui.TermWidth()
	height := ui.TermHeight()

	prepareDrawPage[currentScreen](configuration)
	ui.Render(drawPage[currentScreen](configuration, width, height)...)

	uiEvents := ui.PollEvents()
	ticker := time.NewTicker(time.Second).C

	for {
		select {
		case e := <-uiEvents:
			{
				savedScreen := currentScreen
				fmt.Printf("%V", e)
				if e.Type == ui.KeyboardEvent {
					if e.ID == "<C-c>" {
						goto exitMainLoop
					} else {
						handleKeyboard(configuration, e)
					}
				} else if e.Type == ui.MouseEvent {
					handleMouse(configuration, e)
					ui.Render(drawPage[currentScreen](configuration, width, height)...)
				} else if e.Type == ui.ResizeEvent {
					payload := e.Payload.(ui.Resize)
					width = payload.Width
					height = payload.Height
					ui.Render(drawPage[currentScreen](configuration, width, height)...)
				}
				// refresh screen after keyboard input or redraw screen entirely + reset state if we have changed screens
				if savedScreen != currentScreen {
					prepareDrawPage[currentScreen](configuration)
					// reset modes
					if currentScreen == Search && (savedScreen != PodcastDetail) {
						currentMode = Insert
					} else {
						currentMode = Normal
					}
					// reset text and selected if not transitioning between detail screen
					// TODO make cursor per screen
					if (currentScreen != PodcastDetail) && (savedScreen != PodcastDetail) {
						userTextBuffer = ""
						currentSelected = 0
					}
					// save last screen
					previousScreen = savedScreen
				}
				ui.Render(drawPage[currentScreen](configuration, width, height)...)
			}
		case <-ticker:
			if GetPlayerState() == Play {
				ui.Render(producePlayerWidget(configuration, width, height))
			}
		}
	}
exitMainLoop:
}
<commit_msg>add refreshing playing<commit_after>package main

import (
	ui "github.com/gizak/termui"
	"strings"
	"time"
)

func switchToSelectedPodcastScreen(configuration *Configuration) {
	podcasts := getCurrentPagePodcasts()
	if currentSelected >= len(podcasts) || currentSelected < 0 {
		return
	}
	currentSelectedPodcast = podcasts[currentSelected]
	currentScreen = PodcastDetail
}

func searchPodcastsFromTui(configuration *Configuration) {
	// search TODO use go()
	currentSelected = 0
	var err error
	searchString := strings.Replace(userTextBuffer, "\n", "", -1)
	searchString = strings.Trim(searchString, "\n\t")
	searchString = strings.Replace(searchString, " ", "+", -1) //replace spaces with plus to not break everything
	currentPodcastsInBuffers[currentScreen], err = searchItunes(searchString)
	if err != nil {
		userTextBuffer = "error searching! " + err.Error()
	}
}

func enterPressedHome(configuration *Configuration) {
	// TODO refactor into two functions
	if currentMode == Normal {
		switchToSelectedPodcastScreen(configuration)
	}
}

func enterPressedSearch(configuration *Configuration) {
	if currentMode == Normal {
		switchToSelectedPodcastScreen(configuration)
	} else {
		searchPodcastsFromTui(configuration)
	}
}

func enterPressedDownloaded(configuration *Configuration) {
	podcasts := getCurrentPagePodcastEpisodes()
	if currentSelected >= len(podcasts) || currentSelected < 0 {
		return
	}
	SetPlaying(podcasts[currentSelected].StorageLocation)
	SetPlayerState(Play)
}

func enterPressedPodcastDetail(configuration *Configuration) {
	podcasts := getCurrentPagePodcastEpisodes()
	if currentSelected >= len(podcasts) || currentSelected < 0 {
		return
	}
	if podcastIsDownloaded(configuration, podcasts[currentSelected]) {
		return
	}
	// TODO fix this race condition/bad configuration management.
	go func() {
		configuration, err := downloadPodcast(configuration, currentSelectedPodcast, podcasts[currentSelected])
		if err == nil {
			writeConfig(configuration)
		}
	}()
}

func escapePressedHome(configuration *Configuration) {

}

func escapePressedSearch(configuration *Configuration) {

}

func escapePressedDownloaded(configuration *Configuration) {

}

func actionPressedHome(configuration *Configuration) {

}

func actionPressedSearch(configuration *Configuration) {
	subscribedKey := -1
	podcasts := getCurrentPagePodcasts()
	if currentSelected >= len(podcasts) || currentSelected < 0 {
		return
	}
	selectedPodcast := podcasts[currentSelected]
	// check if it's already part of the configuration
	for key, value := range configuration.Subscribed {
		if selectedPodcast.ArtistName == value.ArtistName && selectedPodcast.CollectionName == value.CollectionName {
			subscribedKey = key
		}
	}
	if subscribedKey != -1 {
		configuration.Subscribed = append(configuration.Subscribed[:subscribedKey], configuration.Subscribed[subscribedKey+1:]...)
	} else {
		configuration.Subscribed = append(configuration.Subscribed, selectedPodcast) //now subscribe by adding it to the subscribed list
	}
	writeConfig(configuration)
}

func actionPressedDownloaded(configuration *Configuration) {

}

func actionPressedPodcastDetail(configuration *Configuration) {

}

func upPressedHome(configuration *Configuration) {
	if currentSelected > 0 {
		currentSelected--
	}
}

func upPressedSearch(configuration *Configuration) {
	if currentSelected > 0 {
		currentSelected--
	} else {
		currentMode = Insert
	}
}

func upPressedDownloaded(configuration *Configuration) {
	if currentSelected > 0 {
		currentSelected--
	}
}

func upPressedPodcastDetail(configuration *Configuration) {
	if currentSelected > 0 {
		currentSelected--
	}
}

func downPressedHome(configuration *Configuration) {
	if currentSelected < currentListSize-1 {
		currentSelected++
	}
}

func downPressedSearch(configuration *Configuration) {
	if currentMode == Insert && len(userTextBuffer) > 0 {
		searchPodcastsFromTui(configuration)
	} else if currentSelected < currentListSize-1 {
		currentSelected++
	}
}

func downPressedDownloaded(configuration *Configuration) {
	if currentSelected < currentListSize-1 {
		currentSelected++
	}
}

func downPressedPodcastDetail(configuration *Configuration) {
	if currentSelected < currentListSize-1 {
		currentSelected++
	}
}

func searchPressedHome(configuration *Configuration) {
	userTextBuffer = ""
	currentMode = Insert
}

func searchPressedSearch(configuration *Configuration) {
	userTextBuffer = ""
	currentMode = Insert
}

func searchPressedDownloaded(configuration *Configuration) {
	userTextBuffer = ""
	currentMode = Insert
}

func handleEventsGlobal(configuration *Configuration, event ui.Event) bool {
	if event.ID == "<Enter>" {
		enterPressed[currentScreen](configuration)
		// reset mode on enter after the action is done
		currentMode = Normal
	} else if event.ID == "<Escape>" {
		// reset mode on Escape as well as possibly do an action
		currentMode = Normal
		escapePressed[currentScreen](configuration)
	} else if (event.ID == configuration.LeftKeybind && currentMode == Normal) || event.ID == "<Left>" {
		transitionScreen(leftTransitions, currentScreen)
	} else if (event.ID == configuration.RightKeybind && currentMode == Normal) || event.ID == "<Right>" {
		transitionScreen(rightTransitions, currentScreen)
	} else if (event.ID == configuration.UpKeybind && currentMode == Normal) || event.ID == "<Up>" {
		upPressed[currentScreen](configuration)
	} else if (event.ID == configuration.DownKeybind && currentMode == Normal) || event.ID == "<Down>" {
		downPressed[currentScreen](configuration)
		currentMode = Normal
	} else {
		// nothing matches, return false
		return false
	}
	// matches, return true
	return true
}

func handleKeyboard(configuration *Configuration, event ui.Event) {
	if handleEventsGlobal(configuration, event) {
		// handled
	} else if currentMode == Insert {
		if event.ID == "<Backspace>" || event.ID == "<Delete>" || event.ID == "C-8>" {
			if len(userTextBuffer) > 0 {
				userTextBuffer = userTextBuffer[:len(userTextBuffer)-1]
			}
		} else if event.ID == "<Space>" {
			userTextBuffer += " "
		} else if event.ID == "<Tab>" {
			userTextBuffer += "	"
		} else if len(event.ID) > 0 && string([]rune(event.ID)[0]) == "<" {
			// do not do anything if it's one of the other control keys
		} else {
			userTextBuffer += event.ID
		}
	} else if event.ID == configuration.PlayKeybind {
		TogglePlayerState()
	} else if event.ID == configuration.SearchKeybind {
		searchPressed[currentScreen](configuration)
	} else if event.ID == configuration.ActionKeybind {
		actionPressed[currentScreen](configuration)
	}
}

func handleMouse(configuration *Configuration, event ui.Event) {
	if event.ID == "<MouseWheelUp>" {
		upPressed[currentScreen](configuration)
	} else if event.ID == "<MouseWheelDown>" {
		downPressed[currentScreen](configuration)
	} else if event.ID == "<MouseLeft>" {
		enterPressed[currentScreen](configuration)
	} else if event.ID == "<MouseRight>" && currentScreen == PodcastDetail {
		transitionScreen(leftTransitions, currentScreen)
	}
}

func tuiMainLoop(configuration *Configuration) {
	width := ui.TermWidth()
	height := ui.TermHeight()

	prepareDrawPage[currentScreen](configuration)
	ui.Render(drawPage[currentScreen](configuration, width, height)...)

	uiEvents := ui.PollEvents()
	ticker := time.NewTicker(time.Second).C

	for {
		select {
		case e := <-uiEvents:
			{
				savedScreen := currentScreen
				if e.Type == ui.KeyboardEvent {
					if e.ID == "<C-c>" {
						goto exitMainLoop
					} else {
						handleKeyboard(configuration, e)
					}
				} else if e.Type == ui.MouseEvent {
					handleMouse(configuration, e)
					ui.Render(drawPage[currentScreen](configuration, width, height)...)
				} else if e.Type == ui.ResizeEvent {
					payload := e.Payload.(ui.Resize)
					width = payload.Width
					height = payload.Height
					ui.Render(drawPage[currentScreen](configuration, width, height)...)
				}
				// refresh screen after keyboard input or redraw screen entirely + reset state if we have changed screens
				if savedScreen != currentScreen {
					prepareDrawPage[currentScreen](configuration)
					// reset modes
					if currentScreen == Search && (savedScreen != PodcastDetail) {
						currentMode = Insert
					} else {
						currentMode = Normal
					}
					// reset text and selected if not transitioning between detail screen
					// TODO make cursor per screen
					if (currentScreen != PodcastDetail) && (savedScreen != PodcastDetail) {
						userTextBuffer = ""
						currentSelected = 0
					}
					// save last screen
					previousScreen = savedScreen
				}
				ui.Render(drawPage[currentScreen](configuration, width, height)...)
			}
		case <-ticker:
			if GetPlayerState() == Play {
				ui.Render(producePlayerWidget(configuration, width, height))
			}
		}
	}
exitMainLoop:
}
<|endoftext|>


<commit_before>package nim

import (
	"github.com/prasmussen/glot-code-runner/cmd"
	"path/filepath"
)

func Run(files []string, stdin string) (string, string, error) {
	workDir := filepath.Dir(files[0])
	return cmd.RunStdin(workDir, stdin, "nim", "--verbosity:0", "compile", "--run", files[0])
}
<commit_msg>Nim: disable hints<commit_after>package nim

import (
	"github.com/prasmussen/glot-code-runner/cmd"
	"path/filepath"
)

func Run(files []string, stdin string) (string, string, error) {
	workDir := filepath.Dir(files[0])
	return cmd.RunStdin(workDir, stdin, "nim", "--hints:off", "--verbosity:0", "compile", "--run", files[0])
}
<|endoftext|>


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

// Package shadertools wraps around external C code for manipulating shaders.
package shadertools

//#include "cc/libmanager.h"
//#include <stdlib.h>
//#include "spirv_reflect.h"
import "C"

import (
	"fmt"
	"sort"
	"strings"
	"sync"
	"unsafe"

	"github.com/google/gapid/core/fault"
	"github.com/google/gapid/core/text"
)

var mutex sync.Mutex

// ShaderType is the enumerator of shader types.
type ShaderType int

const (
	TypeVertex         = ShaderType(C.VERTEX)
	TypeTessControl    = ShaderType(C.TESS_CONTROL)
	TypeTessEvaluation = ShaderType(C.TESS_EVALUATION)
	TypeGeometry       = ShaderType(C.GEOMETRY)
	TypeFragment       = ShaderType(C.FRAGMENT)
	TypeCompute        = ShaderType(C.COMPUTE)
)

// ClientType is the enumerator of client types.
type ClientType int

const (
	OpenGL   = ClientType(C.OPENGL)
	OpenGLES = ClientType(C.OPENGLES)
	Vulkan   = ClientType(C.VULKAN)
)

func (t ShaderType) String() string {
	switch t {
	case TypeVertex:
		return "Vertex"
	case TypeTessControl:
		return "TessControl"
	case TypeTessEvaluation:
		return "TessEvaluation"
	case TypeGeometry:
		return "Geometry"
	case TypeFragment:
		return "Fragment"
	case TypeCompute:
		return "Compute"
	default:
		return "Unknown"
	}
}

// DisassembleSpirvBinary disassembles the given SPIR-V binary words by calling
// SPIRV-Tools and returns the disassembly. Returns an empty string if
// diassembling fails.
func DisassembleSpirvBinary(words []uint32) string {
	source := ""
	if len(words) > 0 {
		spirv := C.getDisassembleText((*C.uint32_t)(&words[0]), C.size_t(len(words)))
		source = C.GoString(spirv)
		C.deleteDisassembleText(spirv)
	}

	return source
}

// AssembleSpirvText assembles the given SPIR-V text chars by calling
// SPIRV-Tools and returns the slice for the encoded binary. Returns nil
// if assembling fails.
func AssembleSpirvText(chars string) []uint32 {
	text := C.CString(chars)
	spirv := C.assembleToBinary(text)
	C.free(unsafe.Pointer(text))

	if spirv == nil {
		return nil
	}

	count := uint64(spirv.words_num)
	words := make([]uint32, count)
	// TODO: Remove the following hack and encoding the data without using unsafe.
	data := (*[1 << 30]uint32)(unsafe.Pointer(spirv.words))[:count:count]
	copy(words, data)
	C.deleteBinary(spirv)

	return words
}

// CompileOptions controls how CompileGlsl compile its passed-in GLSL source code.
type CompileOptions struct {
	// The type of shader.
	ShaderType ShaderType
	// Either OpenGL, OpenGLES or Vulkan
	ClientType ClientType
	// Shader source preamble.
	Preamble string
}

// CompileGlsl compiles GLSL source code to SPIR-V binary words.
func CompileGlsl(source string, o CompileOptions) ([]uint32, error) {
	toFree := []unsafe.Pointer{}
	defer func() {
		for _, ptr := range toFree {
			C.free(ptr)
		}
	}()
	mutex.Lock()
	defer mutex.Unlock()

	cstr := func(s string) *C.char {
		out := C.CString(s)
		toFree = append(toFree, unsafe.Pointer(out))
		return out
	}

	opts := C.struct_compile_options_t{
		shader_type: C.shader_type(o.ShaderType),
		client_type: C.client_type(o.ClientType),
		preamble:    cstr(o.Preamble),
	}

	result := C.compileGlsl(cstr(source), &opts)
	defer C.deleteCompileResult(result)

	count := uint64(result.binary.words_num)
	words := make([]uint32, count)
	if result.ok {
		// TODO: Remove the following hack and encoding the data without using unsafe.
		data := (*[1 << 30]uint32)(unsafe.Pointer(result.binary.words))[:count:count]
		copy(words, data)
		return words, nil
	}
	msg := []string{
		fmt.Sprintf("Failed to compile %v shader.", o.ShaderType),
	}
	if m := C.GoString(result.message); len(m) > 0 {
		msg = append(msg, m)
	}
	msg = append(msg, "Source:", text.LineNumber(source))
	if len(o.Preamble) > 0 {
		msg = append(msg, "Preamble:", text.LineNumber(o.Preamble))
	}
	return words, fault.Const(strings.Join(msg, "\n"))
}

type DescriptorSets map[uint32]DescriptorSet
type DescriptorSet []DescriptorBinding

type DescriptorBinding struct {
	Set             uint32
	Binding         uint32
	SpirvId         uint32
	DescriptorType  uint32
	DescriptorCount uint32
	ShaderStage     uint32
}

func descriptorBindingLess(a DescriptorBinding, b DescriptorBinding) bool {
	if a.Set != b.Set {
		return a.Set < b.Set
	}
	if a.Binding != b.Binding {
		return a.Binding < b.Binding
	}
	// SpirvId is a unique identifier so we don't need to keep comparing after this
	return a.SpirvId < b.SpirvId
}

// ParseAllDescriptorSets determines what descriptor sets are implied by the
// shader, for all entry points of the shader.
func ParseAllDescriptorSets(shader []uint32) (map[string]DescriptorSets, error) {
	out := make(map[string]DescriptorSets)
	spvReflectErr := func(res C.SpvReflectResult) error {
		if res == C.SPV_REFLECT_RESULT_SUCCESS {
			return nil
		}
		return fmt.Errorf("SPIRV-Reflect failed with error code %v\n", res)
	}
	module := C.SpvReflectShaderModule{}

	shaderPtr := unsafe.Pointer(nil)
	if len(shader) > 0 {
		shaderPtr = unsafe.Pointer(&shader[0])
	}
	if err := spvReflectErr(C.spvReflectCreateShaderModule(
		C.size_t(len(shader)*4),
		shaderPtr,
		&module)); err != nil {
		return nil, err
	}
	defer C.spvReflectDestroyShaderModule(&module)

	nEntryPoints := module.entry_point_count
	entryPoints := module.entry_points

	for i := uint32(0); i < uint32(nEntryPoints); i++ {
		// Access module.entry_points[i]: use C pointer as array, do pointer
		// arithmetic as documented in https://golang.org/pkg/unsafe/#Pointer
		entryPointStruct := (*C.SpvReflectEntryPoint)(unsafe.Pointer(uintptr(unsafe.Pointer(entryPoints)) + uintptr(i)*unsafe.Sizeof(*(entryPoints))))

		setCount := C.uint32_t(0)
		if err := spvReflectErr(C.spvReflectEnumerateEntryPointDescriptorSets(
			&module,
			entryPointStruct.name,
			&setCount,
			nil)); err != nil {
			return nil, err
		}
		sets := make([]*C.SpvReflectDescriptorSet, setCount)
		setsPtr := unsafe.Pointer(nil)
		if setCount > 0 {
			setsPtr = unsafe.Pointer(&sets[0])
		}
		if err := spvReflectErr(C.spvReflectEnumerateEntryPointDescriptorSets(
			&module,
			entryPointStruct.name,
			&setCount,
			(**C.SpvReflectDescriptorSet)(setsPtr),
		)); err != nil {
			return nil, err
		}

		res := DescriptorSets{}
		for _, set := range sets {
			bindings := make(DescriptorSet, set.binding_count)
			for i := C.uint32_t(0); i < set.binding_count; i++ {
				bindingPtr := uintptr(unsafe.Pointer(set.bindings)) +
					uintptr(i)*unsafe.Sizeof(*set.bindings)
				binding := *(**C.SpvReflectDescriptorBinding)(unsafe.Pointer(bindingPtr))
				// If it's an array, need to get total descriptor count
				descriptorCount := C.uint32_t(1)
				for j := C.uint32_t(0); j < binding.array.dims_count; j++ {
					descriptorCount *= binding.array.dims[j]
				}
				bindings[i] = DescriptorBinding{
					Set:             uint32(binding.set),
					Binding:         uint32(binding.binding),
					SpirvId:         uint32(binding.spirv_id),
					DescriptorType:  uint32(binding.descriptor_type),
					DescriptorCount: uint32(descriptorCount),
					ShaderStage:     uint32(entryPointStruct.shader_stage),
				}
			}
			sort.Slice(bindings, func(i, j int) bool {
				return descriptorBindingLess(bindings[i], bindings[j])
			})
			res[uint32(set.set)] = bindings
		}
		out[C.GoString(entryPointStruct.name)] = res
	}

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

// Package shadertools wraps around external C code for manipulating shaders.
package shadertools

//#include "cc/libmanager.h"
//#include <stdlib.h>
//#include "spirv_reflect.h"
import "C"

import (
	"errors"
	"fmt"
	"sort"
	"strings"
	"sync"
	"unsafe"

	"github.com/google/gapid/core/fault"
	"github.com/google/gapid/core/text"
)

var mutex sync.Mutex

// ShaderType is the enumerator of shader types.
type ShaderType int

const (
	TypeVertex         = ShaderType(C.VERTEX)
	TypeTessControl    = ShaderType(C.TESS_CONTROL)
	TypeTessEvaluation = ShaderType(C.TESS_EVALUATION)
	TypeGeometry       = ShaderType(C.GEOMETRY)
	TypeFragment       = ShaderType(C.FRAGMENT)
	TypeCompute        = ShaderType(C.COMPUTE)
)

// ClientType is the enumerator of client types.
type ClientType int

const (
	OpenGL   = ClientType(C.OPENGL)
	OpenGLES = ClientType(C.OPENGLES)
	Vulkan   = ClientType(C.VULKAN)
)

func (t ShaderType) String() string {
	switch t {
	case TypeVertex:
		return "Vertex"
	case TypeTessControl:
		return "TessControl"
	case TypeTessEvaluation:
		return "TessEvaluation"
	case TypeGeometry:
		return "Geometry"
	case TypeFragment:
		return "Fragment"
	case TypeCompute:
		return "Compute"
	default:
		return "Unknown"
	}
}

// DisassembleSpirvBinary disassembles the given SPIR-V binary words by calling
// SPIRV-Tools and returns the disassembly. Returns an empty string if
// diassembling fails.
func DisassembleSpirvBinary(words []uint32) string {
	source := ""
	if len(words) > 0 {
		spirv := C.getDisassembleText((*C.uint32_t)(&words[0]), C.size_t(len(words)))
		source = C.GoString(spirv)
		C.deleteDisassembleText(spirv)
	}

	return source
}

// AssembleSpirvText assembles the given SPIR-V text chars by calling
// SPIRV-Tools and returns the slice for the encoded binary. Returns nil
// if assembling fails.
func AssembleSpirvText(chars string) []uint32 {
	text := C.CString(chars)
	spirv := C.assembleToBinary(text)
	C.free(unsafe.Pointer(text))

	if spirv == nil {
		return nil
	}

	count := uint64(spirv.words_num)
	words := make([]uint32, count)
	// TODO: Remove the following hack and encoding the data without using unsafe.
	data := (*[1 << 30]uint32)(unsafe.Pointer(spirv.words))[:count:count]
	copy(words, data)
	C.deleteBinary(spirv)

	return words
}

// CompileOptions controls how CompileGlsl compile its passed-in GLSL source code.
type CompileOptions struct {
	// The type of shader.
	ShaderType ShaderType
	// Either OpenGL, OpenGLES or Vulkan
	ClientType ClientType
	// Shader source preamble.
	Preamble string
}

// CompileGlsl compiles GLSL source code to SPIR-V binary words.
func CompileGlsl(source string, o CompileOptions) ([]uint32, error) {
	toFree := []unsafe.Pointer{}
	defer func() {
		for _, ptr := range toFree {
			C.free(ptr)
		}
	}()
	mutex.Lock()
	defer mutex.Unlock()

	cstr := func(s string) *C.char {
		out := C.CString(s)
		toFree = append(toFree, unsafe.Pointer(out))
		return out
	}

	opts := C.struct_compile_options_t{
		shader_type: C.shader_type(o.ShaderType),
		client_type: C.client_type(o.ClientType),
		preamble:    cstr(o.Preamble),
	}

	result := C.compileGlsl(cstr(source), &opts)
	defer C.deleteCompileResult(result)

	count := uint64(result.binary.words_num)
	words := make([]uint32, count)
	if result.ok {
		// TODO: Remove the following hack and encoding the data without using unsafe.
		data := (*[1 << 30]uint32)(unsafe.Pointer(result.binary.words))[:count:count]
		copy(words, data)
		return words, nil
	}
	msg := []string{
		fmt.Sprintf("Failed to compile %v shader.", o.ShaderType),
	}
	if m := C.GoString(result.message); len(m) > 0 {
		msg = append(msg, m)
	}
	msg = append(msg, "Source:", text.LineNumber(source))
	if len(o.Preamble) > 0 {
		msg = append(msg, "Preamble:", text.LineNumber(o.Preamble))
	}
	return words, fault.Const(strings.Join(msg, "\n"))
}

type DescriptorSets map[uint32]DescriptorSet
type DescriptorSet []DescriptorBinding

type DescriptorBinding struct {
	Set             uint32
	Binding         uint32
	SpirvId         uint32
	DescriptorType  uint32
	DescriptorCount uint32
	ShaderStage     uint32
}

func descriptorBindingLess(a DescriptorBinding, b DescriptorBinding) bool {
	if a.Set != b.Set {
		return a.Set < b.Set
	}
	if a.Binding != b.Binding {
		return a.Binding < b.Binding
	}
	// SpirvId is a unique identifier so we don't need to keep comparing after this
	return a.SpirvId < b.SpirvId
}

func ExtractDebugSource(shader []uint32) (string, error) {
	spvReflectErr := func(res C.SpvReflectResult) error {
		if res == C.SPV_REFLECT_RESULT_SUCCESS {
			return nil
		}
		return fmt.Errorf("SPIRV-Reflect failed with error code %v\n", res)
	}

	if len(shader) == 0 {
		return "", errors.New("Empty Shader")
	}

	module := C.SpvReflectShaderModule{}

	shaderPtr := unsafe.Pointer(&shader[0])

	err := spvReflectErr(C.spvReflectCreateShaderModule(C.size_t(len(shader)*4), shaderPtr, &module))
	if err != nil {
		return "", err
	}
	defer C.spvReflectDestroyShaderModule(&module)

	if module.source_source == nil {
		return "", errors.New("No Source File Detected")
	}

	return C.GoString(module.source_source), nil
}

// ParseAllDescriptorSets determines what descriptor sets are implied by the
// shader, for all entry points of the shader.
func ParseAllDescriptorSets(shader []uint32) (map[string]DescriptorSets, error) {
	out := make(map[string]DescriptorSets)
	spvReflectErr := func(res C.SpvReflectResult) error {
		if res == C.SPV_REFLECT_RESULT_SUCCESS {
			return nil
		}
		return fmt.Errorf("SPIRV-Reflect failed with error code %v\n", res)
	}
	module := C.SpvReflectShaderModule{}

	shaderPtr := unsafe.Pointer(nil)
	if len(shader) > 0 {
		shaderPtr = unsafe.Pointer(&shader[0])
	}
	if err := spvReflectErr(C.spvReflectCreateShaderModule(
		C.size_t(len(shader)*4),
		shaderPtr,
		&module)); err != nil {
		return nil, err
	}
	defer C.spvReflectDestroyShaderModule(&module)

	nEntryPoints := module.entry_point_count
	entryPoints := module.entry_points

	for i := uint32(0); i < uint32(nEntryPoints); i++ {
		// Access module.entry_points[i]: use C pointer as array, do pointer
		// arithmetic as documented in https://golang.org/pkg/unsafe/#Pointer
		entryPointStruct := (*C.SpvReflectEntryPoint)(unsafe.Pointer(uintptr(unsafe.Pointer(entryPoints)) + uintptr(i)*unsafe.Sizeof(*(entryPoints))))

		setCount := C.uint32_t(0)
		if err := spvReflectErr(C.spvReflectEnumerateEntryPointDescriptorSets(
			&module,
			entryPointStruct.name,
			&setCount,
			nil)); err != nil {
			return nil, err
		}
		sets := make([]*C.SpvReflectDescriptorSet, setCount)
		setsPtr := unsafe.Pointer(nil)
		if setCount > 0 {
			setsPtr = unsafe.Pointer(&sets[0])
		}
		if err := spvReflectErr(C.spvReflectEnumerateEntryPointDescriptorSets(
			&module,
			entryPointStruct.name,
			&setCount,
			(**C.SpvReflectDescriptorSet)(setsPtr),
		)); err != nil {
			return nil, err
		}

		res := DescriptorSets{}
		for _, set := range sets {
			bindings := make(DescriptorSet, set.binding_count)
			for i := C.uint32_t(0); i < set.binding_count; i++ {
				bindingPtr := uintptr(unsafe.Pointer(set.bindings)) +
					uintptr(i)*unsafe.Sizeof(*set.bindings)
				binding := *(**C.SpvReflectDescriptorBinding)(unsafe.Pointer(bindingPtr))
				// If it's an array, need to get total descriptor count
				descriptorCount := C.uint32_t(1)
				for j := C.uint32_t(0); j < binding.array.dims_count; j++ {
					descriptorCount *= binding.array.dims[j]
				}
				bindings[i] = DescriptorBinding{
					Set:             uint32(binding.set),
					Binding:         uint32(binding.binding),
					SpirvId:         uint32(binding.spirv_id),
					DescriptorType:  uint32(binding.descriptor_type),
					DescriptorCount: uint32(descriptorCount),
					ShaderStage:     uint32(entryPointStruct.shader_stage),
				}
			}
			sort.Slice(bindings, func(i, j int) bool {
				return descriptorBindingLess(bindings[i], bindings[j])
			})
			res[uint32(set.set)] = bindings
		}
		out[C.GoString(entryPointStruct.name)] = res
	}

	return out, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"
)

type Shape interface {
	Perimeter() float64
}

type Circle struct {
	radius float64
}

func (c Circle) Circumference() float64 {
	return 2 * math.Pi * c.radius
}

func (c Circle) Perimeter() float64 {
	return c.Circumference()
}

type ConvexPolygon interface {
	NumSides() int
}

func NumDiagonals(poly ConvexPolygon) int {
	return poly.NumSides() * (poly.NumSides() - 3) / 2
}

type Rectangle struct {
	width, height float64
}

func (rect Rectangle) NumSides() int {
	return 4
}

func (rect Rectangle) Perimeter() float64 {
	return (rect.width + rect.height) * 2
}

type Equilateral interface {
	NumSides() int
	SideLength() float64
}

func Perimeter(this Equilateral) float64 {
	return this.SideLength() * float64(this.NumSides())
}

type Square struct {
	sideLength float64
}

func (sq Square) SideLength() float64 {
	return sq.sideLength
}

func (sq Square) NumSides() int {
	return 4
}

func (sq Square) Perimeter() float64 {
	return Perimeter(sq)
}

func main() {
	round := Circle{radius: 1}
	fmt.Println("The perimeter of a circle with unit radius is", round.Perimeter())
    rect := Rectangle{16, 9}
	fmt.Println("The perimeter of a 16x9 rectangle is", rect.Perimeter())
	box := Square{sideLength: 1}
	fmt.Println("The perimeter of a unit square is", box.Perimeter())
    fmt.Println("A square has", NumDiagonals(box), "diagonals.")
}
<commit_msg>use a constant<commit_after>package main

import (
	"fmt"
	"math"
)

type Shape interface {
	Perimeter() float64
}

type Circle struct {
	radius float64
}

func (c Circle) Circumference() float64 {
	return 2 * math.Pi * c.radius
}

func (c Circle) Perimeter() float64 {
	return c.Circumference()
}

type ConvexPolygon interface {
	NumSides() int
}

func NumDiagonals(poly ConvexPolygon) int {
	return poly.NumSides() * (poly.NumSides() - 3) / 2
}

const Quad = 4

type Rectangle struct {
	width, height float64
}

func (rect Rectangle) NumSides() int {
	return Quad
}

func (rect Rectangle) Perimeter() float64 {
	return (rect.width + rect.height) * 2
}

type Equilateral interface {
	NumSides() int
	SideLength() float64
}

func Perimeter(this Equilateral) float64 {
	return this.SideLength() * float64(this.NumSides())
}

type Square struct {
	sideLength float64
}

func (sq Square) SideLength() float64 {
	return sq.sideLength
}

func (sq Square) NumSides() int {
	return Quad
}

func (sq Square) Perimeter() float64 {
	return Perimeter(sq)
}

func main() {
	round := Circle{radius: 1}
	fmt.Println("The perimeter of a circle with unit radius is", round.Perimeter())
    rect := Rectangle{16, 9}
	fmt.Println("The perimeter of a 16x9 rectangle is", rect.Perimeter())
	box := Square{sideLength: 1}
	fmt.Println("The perimeter of a unit square is", box.Perimeter())
    fmt.Println("A square has", NumDiagonals(box), "diagonals.")
}
<|endoftext|>


<commit_before>package layout

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/containers/image/manifest"
	"github.com/containers/image/types"
	imgspec "github.com/opencontainers/image-spec/specs-go"
	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
)

type ociImageDestination struct {
	ref ociReference
}

// newImageDestination returns an ImageDestination for writing to an existing directory.
func newImageDestination(ref ociReference) types.ImageDestination {
	return &ociImageDestination{ref: ref}
}

// Reference returns the reference used to set up this destination.  Note that this should directly correspond to user's intent,
// e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects.
func (d *ociImageDestination) Reference() types.ImageReference {
	return d.ref
}

// Close removes resources associated with an initialized ImageDestination, if any.
func (d *ociImageDestination) Close() {
}

func (d *ociImageDestination) SupportedManifestMIMETypes() []string {
	return []string{
		imgspecv1.MediaTypeImageManifest,
		manifest.DockerV2Schema2MediaType,
	}
}

// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
func (d *ociImageDestination) SupportsSignatures() error {
	return fmt.Errorf("Pushing signatures for OCI images is not supported")
}

// PutBlob writes contents of stream and returns its computed digest and size.
// A digest can be optionally provided if known, the specific image destination can decide to play with it or not.
// The length of stream is expected to be expectedSize; if expectedSize == -1, it is not known.
// WARNING: The contents of stream are being verified on the fly.  Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
// to any other readers for download using the supplied digest.
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
func (d *ociImageDestination) PutBlob(stream io.Reader, _ string, expectedSize int64) (string, int64, error) {
	if err := ensureDirectoryExists(d.ref.dir); err != nil {
		return "", -1, err
	}
	blobFile, err := ioutil.TempFile(d.ref.dir, "oci-put-blob")
	if err != nil {
		return "", -1, err
	}
	succeeded := false
	defer func() {
		blobFile.Close()
		if !succeeded {
			os.Remove(blobFile.Name())
		}
	}()

	h := sha256.New()
	tee := io.TeeReader(stream, h)

	size, err := io.Copy(blobFile, tee)
	if err != nil {
		return "", -1, err
	}
	computedDigest := "sha256:" + hex.EncodeToString(h.Sum(nil))
	if expectedSize != -1 && size != expectedSize {
		return "", -1, fmt.Errorf("Size mismatch when copying %s, expected %d, got %d", computedDigest, expectedSize, size)
	}
	if err := blobFile.Sync(); err != nil {
		return "", -1, err
	}
	if err := blobFile.Chmod(0644); err != nil {
		return "", -1, err
	}

	blobPath, err := d.ref.blobPath(computedDigest)
	if err != nil {
		return "", -1, err
	}
	if err := ensureParentDirectoryExists(blobPath); err != nil {
		return "", -1, err
	}
	if err := os.Rename(blobFile.Name(), blobPath); err != nil {
		return "", -1, err
	}
	succeeded = true
	return computedDigest, size, nil
}

func createManifest(m []byte) ([]byte, string, error) {
	om := imgspecv1.Manifest{}
	mt := manifest.GuessMIMEType(m)
	switch mt {
	case manifest.DockerV2Schema1MediaType:
		// There a simple reason about not yet implementing this.
		// OCI image-spec assure about backward compatibility with docker v2s2 but not v2s1
		// generating a v2s2 is a migration docker does when upgrading to 1.10.3
		// and I don't think we should bother about this now (I don't want to have migration code here in skopeo)
		return nil, "", fmt.Errorf("can't create OCI manifest from Docker V2 schema 1 manifest")
	case manifest.DockerV2Schema2MediaType:
		if err := json.Unmarshal(m, &om); err != nil {
			return nil, "", err
		}
		om.MediaType = imgspecv1.MediaTypeImageManifest
		for i := range om.Layers {
			om.Layers[i].MediaType = imgspecv1.MediaTypeImageLayer
		}
		om.Config.MediaType = imgspecv1.MediaTypeImageConfig
		b, err := json.Marshal(om)
		if err != nil {
			return nil, "", err
		}
		return b, om.MediaType, nil
	case manifest.DockerV2ListMediaType:
		return nil, "", fmt.Errorf("can't create OCI manifest from Docker V2 schema 2 manifest list")
	case imgspecv1.MediaTypeImageManifestList:
		return nil, "", fmt.Errorf("can't create OCI manifest from OCI manifest list")
	case imgspecv1.MediaTypeImageManifest:
		return m, mt, nil
	}
	return nil, "", fmt.Errorf("Unrecognized manifest media type")
}

func (d *ociImageDestination) PutManifest(m []byte) error {
	// TODO(mitr, runcom): this breaks signatures entirely since at this point we're creating a new manifest
	// and signatures don't apply anymore. Will fix.
	ociMan, mt, err := createManifest(m)
	if err != nil {
		return err
	}
	digest, err := manifest.Digest(ociMan)
	if err != nil {
		return err
	}
	desc := imgspec.Descriptor{}
	desc.Digest = digest
	// TODO(runcom): beaware and add support for OCI manifest list
	desc.MediaType = mt
	desc.Size = int64(len(ociMan))
	data, err := json.Marshal(desc)
	if err != nil {
		return err
	}

	blobPath, err := d.ref.blobPath(digest)
	if err != nil {
		return err
	}
	if err := ioutil.WriteFile(blobPath, ociMan, 0644); err != nil {
		return err
	}
	// TODO(runcom): ugly here?
	if err := ioutil.WriteFile(d.ref.ociLayoutPath(), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0644); err != nil {
		return err
	}
	descriptorPath := d.ref.descriptorPath(d.ref.tag)
	if err := ensureParentDirectoryExists(descriptorPath); err != nil {
		return err
	}
	return ioutil.WriteFile(descriptorPath, data, 0644)
}

func ensureDirectoryExists(path string) error {
	if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
		if err := os.MkdirAll(path, 0755); err != nil {
			return err
		}
	}
	return nil
}

// ensureParentDirectoryExists ensures the parent of the supplied path exists.
func ensureParentDirectoryExists(path string) error {
	return ensureDirectoryExists(filepath.Dir(path))
}

func (d *ociImageDestination) PutSignatures(signatures [][]byte) error {
	if len(signatures) != 0 {
		return fmt.Errorf("Pushing signatures for OCI images is not supported")
	}
	return nil
}

// Commit marks the process of storing the image as successful and asks for the image to be persisted.
// WARNING: This does not have any transactional semantics:
// - Uploaded data MAY be visible to others before Commit() is called
// - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed)
func (d *ociImageDestination) Commit() error {
	return nil
}
<commit_msg>vendor containers/image#84<commit_after>package layout

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/containers/image/manifest"
	"github.com/containers/image/types"
	imgspec "github.com/opencontainers/image-spec/specs-go"
	imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
)

type ociImageDestination struct {
	ref ociReference
}

// newImageDestination returns an ImageDestination for writing to an existing directory.
func newImageDestination(ref ociReference) types.ImageDestination {
	return &ociImageDestination{ref: ref}
}

// Reference returns the reference used to set up this destination.  Note that this should directly correspond to user's intent,
// e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects.
func (d *ociImageDestination) Reference() types.ImageReference {
	return d.ref
}

// Close removes resources associated with an initialized ImageDestination, if any.
func (d *ociImageDestination) Close() {
}

func (d *ociImageDestination) SupportedManifestMIMETypes() []string {
	return []string{
		imgspecv1.MediaTypeImageManifest,
		manifest.DockerV2Schema2MediaType,
	}
}

// SupportsSignatures returns an error (to be displayed to the user) if the destination certainly can't store signatures.
// Note: It is still possible for PutSignatures to fail if SupportsSignatures returns nil.
func (d *ociImageDestination) SupportsSignatures() error {
	return fmt.Errorf("Pushing signatures for OCI images is not supported")
}

// PutBlob writes contents of stream and returns its computed digest and size.
// A digest can be optionally provided if known, the specific image destination can decide to play with it or not.
// The length of stream is expected to be expectedSize; if expectedSize == -1, it is not known.
// WARNING: The contents of stream are being verified on the fly.  Until stream.Read() returns io.EOF, the contents of the data SHOULD NOT be available
// to any other readers for download using the supplied digest.
// If stream.Read() at any time, ESPECIALLY at end of input, returns an error, PutBlob MUST 1) fail, and 2) delete any data stored so far.
func (d *ociImageDestination) PutBlob(stream io.Reader, _ string, expectedSize int64) (string, int64, error) {
	if err := ensureDirectoryExists(d.ref.dir); err != nil {
		return "", -1, err
	}
	blobFile, err := ioutil.TempFile(d.ref.dir, "oci-put-blob")
	if err != nil {
		return "", -1, err
	}
	succeeded := false
	defer func() {
		blobFile.Close()
		if !succeeded {
			os.Remove(blobFile.Name())
		}
	}()

	h := sha256.New()
	tee := io.TeeReader(stream, h)

	size, err := io.Copy(blobFile, tee)
	if err != nil {
		return "", -1, err
	}
	computedDigest := "sha256:" + hex.EncodeToString(h.Sum(nil))
	if expectedSize != -1 && size != expectedSize {
		return "", -1, fmt.Errorf("Size mismatch when copying %s, expected %d, got %d", computedDigest, expectedSize, size)
	}
	if err := blobFile.Sync(); err != nil {
		return "", -1, err
	}
	if err := blobFile.Chmod(0644); err != nil {
		return "", -1, err
	}

	blobPath, err := d.ref.blobPath(computedDigest)
	if err != nil {
		return "", -1, err
	}
	if err := ensureParentDirectoryExists(blobPath); err != nil {
		return "", -1, err
	}
	if err := os.Rename(blobFile.Name(), blobPath); err != nil {
		return "", -1, err
	}
	succeeded = true
	return computedDigest, size, nil
}

func createManifest(m []byte) ([]byte, string, error) {
	om := imgspecv1.Manifest{}
	mt := manifest.GuessMIMEType(m)
	switch mt {
	case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType:
		// There a simple reason about not yet implementing this.
		// OCI image-spec assure about backward compatibility with docker v2s2 but not v2s1
		// generating a v2s2 is a migration docker does when upgrading to 1.10.3
		// and I don't think we should bother about this now (I don't want to have migration code here in skopeo)
		return nil, "", errors.New("can't create an OCI manifest from Docker V2 schema 1 manifest")
	case manifest.DockerV2Schema2MediaType:
		if err := json.Unmarshal(m, &om); err != nil {
			return nil, "", err
		}
		om.MediaType = imgspecv1.MediaTypeImageManifest
		for i := range om.Layers {
			om.Layers[i].MediaType = imgspecv1.MediaTypeImageLayer
		}
		om.Config.MediaType = imgspecv1.MediaTypeImageConfig
		b, err := json.Marshal(om)
		if err != nil {
			return nil, "", err
		}
		return b, om.MediaType, nil
	case manifest.DockerV2ListMediaType:
		return nil, "", errors.New("can't create an OCI manifest from Docker V2 schema 2 manifest list")
	case imgspecv1.MediaTypeImageManifestList:
		return nil, "", errors.New("can't create an OCI manifest from OCI manifest list")
	case imgspecv1.MediaTypeImageManifest:
		return m, mt, nil
	}
	return nil, "", fmt.Errorf("unrecognized manifest media type %q", mt)
}

func (d *ociImageDestination) PutManifest(m []byte) error {
	// TODO(mitr, runcom): this breaks signatures entirely since at this point we're creating a new manifest
	// and signatures don't apply anymore. Will fix.
	ociMan, mt, err := createManifest(m)
	if err != nil {
		return err
	}
	digest, err := manifest.Digest(ociMan)
	if err != nil {
		return err
	}
	desc := imgspec.Descriptor{}
	desc.Digest = digest
	// TODO(runcom): beaware and add support for OCI manifest list
	desc.MediaType = mt
	desc.Size = int64(len(ociMan))
	data, err := json.Marshal(desc)
	if err != nil {
		return err
	}

	blobPath, err := d.ref.blobPath(digest)
	if err != nil {
		return err
	}
	if err := ioutil.WriteFile(blobPath, ociMan, 0644); err != nil {
		return err
	}
	// TODO(runcom): ugly here?
	if err := ioutil.WriteFile(d.ref.ociLayoutPath(), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0644); err != nil {
		return err
	}
	descriptorPath := d.ref.descriptorPath(d.ref.tag)
	if err := ensureParentDirectoryExists(descriptorPath); err != nil {
		return err
	}
	return ioutil.WriteFile(descriptorPath, data, 0644)
}

func ensureDirectoryExists(path string) error {
	if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
		if err := os.MkdirAll(path, 0755); err != nil {
			return err
		}
	}
	return nil
}

// ensureParentDirectoryExists ensures the parent of the supplied path exists.
func ensureParentDirectoryExists(path string) error {
	return ensureDirectoryExists(filepath.Dir(path))
}

func (d *ociImageDestination) PutSignatures(signatures [][]byte) error {
	if len(signatures) != 0 {
		return fmt.Errorf("Pushing signatures for OCI images is not supported")
	}
	return nil
}

// Commit marks the process of storing the image as successful and asks for the image to be persisted.
// WARNING: This does not have any transactional semantics:
// - Uploaded data MAY be visible to others before Commit() is called
// - Uploaded data MAY be removed or MAY remain around if Close() is called without Commit() (i.e. rollback is allowed but not guaranteed)
func (d *ociImageDestination) Commit() error {
	return nil
}
<|endoftext|>


<commit_before>package model

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

var toIdRegexp = regexp.MustCompile("[^a-z0-9]+")

func assertTrue(e bool, msg, location string) {
	if !e {
		panic(fmt.Sprintf("%s: %s", location, msg))
	}
}

func fileExists(filePath string) bool {
	if _, err := os.Stat(filepath.FromSlash(filePath)); err != nil {
		if os.IsNotExist(err) {
			return false
		} else {
			panic(err)
		}
	}
	return true
}

func isDirectory(file string) bool {
	s, e := os.Stat(filepath.FromSlash(file))
	panicOnError(e)
	return s.IsDir()
}

func panicOnError(e error) {
	if e != nil {
		panic(e)
	}
}

func toId(v string) string {
	return strings.Trim(toIdRegexp.ReplaceAllLiteralString(strings.ToLower(v), "-"), "-")
}

func absPath(p, basePath string) string {
	if len(p) > 0 && p[0:1] == "/" {
		return path.Clean(p)
	} else {
		return path.Join(path.Dir(basePath), p)
	}
}

func relPath(p, basePath string) string {
	p = path.Clean(p)
	if len(p) == 0 || p[0:1] == "/" {
		baseDir := path.Clean(path.Dir(basePath))
		switch {
		case p == baseDir:
			p = "."
		case strings.Index(p, baseDir+"/") == 0:
			p = p[len(baseDir)+1:]
		}
	}
	if isPath(p) {
		return p
	} else {
		return "./" + p
	}
}

func isPath(v string) bool {
	return "." == v || (len(v) > 0 && v[0:1] == "/") || (len(v) > 1 && v[0:2] == "./")
}
<commit_msg>fixed volume mounts<commit_after>package model

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

var toIdRegexp = regexp.MustCompile("[^a-z0-9]+")

func assertTrue(e bool, msg, location string) {
	if !e {
		panic(fmt.Sprintf("%s: %s", location, msg))
	}
}

func fileExists(filePath string) bool {
	if _, err := os.Stat(filepath.FromSlash(filePath)); err != nil {
		if os.IsNotExist(err) {
			return false
		} else {
			panic(err)
		}
	}
	return true
}

func isDirectory(file string) bool {
	s, e := os.Stat(filepath.FromSlash(file))
	panicOnError(e)
	return s.IsDir()
}

func panicOnError(e error) {
	if e != nil {
		panic(e)
	}
}

func toId(v string) string {
	return strings.Trim(toIdRegexp.ReplaceAllLiteralString(strings.ToLower(v), "-"), "-")
}

func absPath(p, basePath string) string {
	if len(p) > 0 && p[0:1] == "/" {
		return path.Clean(p)
	} else {
		return path.Join(path.Dir(basePath), p)
	}
}

func relPath(p, basePath string) string {
	p = path.Clean(p)
	if len(p) == 0 || p[0:1] == "/" {
		baseDir := path.Clean(path.Dir(basePath))
		switch {
		case p == baseDir:
			p = "."
		case strings.Index(p, baseDir+"/") == 0:
			p = p[len(baseDir)+1:]
		}
	}
	if isPath(p) {
		return p
	} else {
		return "./" + p
	}
}

func isPath(v string) bool {
	return "." == v || (len(v) > 0 && v[0:1] == "/") || (len(v) > 1 && v[0:2] == "./") || (len(v) > 2 && v[0:3] == "../")
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Program aedeploy assists with deploying Go Managed VM apps to production.
// A temporary directory is created; the app, its subdirectories, and all its
// dependencies from $GOPATH are copied into the directory; then the app
// is deployed to production with the provided command.
//
// The app must be in "package main".
//
// This command must be issued from within the root directory of the app
// (where the app.yaml file is located).
package main

import (
	"flag"
	"fmt"
	"go/build"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	skipFiles = map[string]bool{
		".git":        true,
		".gitconfig":  true,
		".hg":         true,
		".travis.yml": true,
	}

	gopathCache = map[string]string{}
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\t%s gcloud --verbosity debug preview app deploy --version myversion ./app.yaml\tDeploy app to production\n", os.Args[0])
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 {
		usage()
		os.Exit(1)
	}

	if err := aedeploy(); err != nil {
		fmt.Fprintf(os.Stderr, os.Args[0]+": Error: %v\n", err)
		os.Exit(1)
	}
}

func aedeploy() error {
	tags := []string{"appenginevm"}
	app, err := analyze(tags)
	if err != nil {
		return err
	}

	tmpDir, err := app.bundle()
	if tmpDir != "" {
		defer os.RemoveAll(tmpDir)
	}
	if err != nil {
		return err
	}

	if err := os.Chdir(tmpDir); err != nil {
		return fmt.Errorf("unable to chdir to %v: %v", tmpDir, err)
	}
	return deploy()
}

// deploy calls the provided command to deploy the app from the temporary directory.
func deploy() error {
	cmd := exec.Command(flag.Arg(0), flag.Args()[1:]...)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("unable to run %q: %v", strings.Join(flag.Args(), " "), err)
	}
	return nil
}

type app struct {
	appFiles []string
	imports  map[string]string
}

// analyze checks the app for building with the given build tags and returns
// app files, and a map of full directory import names to original import names.
func analyze(tags []string) (*app, error) {
	ctxt := buildContext(tags)
	appFiles, err := appFiles(ctxt)
	if err != nil {
		return nil, err
	}
	gopath := filepath.SplitList(ctxt.GOPATH)
	im, err := imports(ctxt, ".", gopath)
	return &app{
		appFiles: appFiles,
		imports:  im,
	}, err
}

// buildContext returns the context for building the source.
func buildContext(tags []string) *build.Context {
	return &build.Context{
		GOARCH:    "amd64",
		GOOS:      "linux",
		GOROOT:    build.Default.GOROOT,
		GOPATH:    build.Default.GOPATH,
		Compiler:  build.Default.Compiler,
		BuildTags: append(build.Default.BuildTags, tags...),
	}
}

// bundle bundles the app into a temporary directory.
func (s *app) bundle() (tmpdir string, err error) {
	workDir, err := ioutil.TempDir("", "aedeploy")
	if err != nil {
		return "", fmt.Errorf("unable to create tmpdir: %v", err)
	}

	for srcDir, importName := range s.imports {
		dstDir := "_gopath/src/" + importName
		if err := copyTree(workDir, dstDir, srcDir); err != nil {
			return workDir, fmt.Errorf("unable to copy directory %v to %v: %v", srcDir, dstDir, err)
		}
	}
	if err := copyTree(workDir, ".", "."); err != nil {
		return workDir, fmt.Errorf("unable to copy root directory to /app: %v", err)
	}
	return workDir, nil
}

// imports returns a map of all import directories (recursively) used by the app.
// The return value maps full directory names to original import names.
func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) {
	pkg, err := ctxt.ImportDir(srcDir, 0)
	if err != nil {
		return nil, err
	}

	// Resolve all non-standard-library imports
	result := make(map[string]string)
	for _, v := range pkg.Imports {
		if !strings.Contains(v, ".") {
			continue
		}
		src, err := findInGopath(v, gopath)
		if err != nil {
			return nil, fmt.Errorf("unable to find import %v in gopath %v: %v", v, gopath, err)
		}
		if _, ok := result[src]; ok { // Already processed
			continue
		}
		result[src] = v
		im, err := imports(ctxt, src, gopath)
		if err != nil {
			return nil, fmt.Errorf("unable to parse package %v: %v", src, err)
		}
		for k, v := range im {
			result[k] = v
		}
	}
	return result, nil
}

// findInGopath searches the gopath for the named import directory.
func findInGopath(dir string, gopath []string) (string, error) {
	if v, ok := gopathCache[dir]; ok {
		return v, nil
	}
	for _, v := range gopath {
		dst := filepath.Join(v, "src", dir)
		if _, err := os.Stat(dst); err == nil {
			gopathCache[dir] = dst
			return dst, nil
		}
	}
	return "", fmt.Errorf("unable to find package %v in gopath %v", dir, gopath)
}

// copyTree copies srcDir to dstDir relative to dstRoot, ignoring skipFiles.
func copyTree(dstRoot, dstDir, srcDir string) error {
	d := filepath.Join(dstRoot, dstDir)
	if err := os.MkdirAll(d, 0755); err != nil {
		return fmt.Errorf("unable to create directory %v: %v", d, err)
	}

	entries, err := ioutil.ReadDir(srcDir)
	if err != nil {
		return fmt.Errorf("unable to read dir %v: %v", srcDir, err)
	}
	for _, entry := range entries {
		n := entry.Name()
		if skipFiles[n] {
			continue
		}
		s := filepath.Join(srcDir, n)
		d := filepath.Join(dstDir, n)
		if entry.IsDir() {
			if err := copyTree(dstRoot, d, s); err != nil {
				return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
			}
			continue
		}
		if err := copyFile(dstRoot, d, s); err != nil {
			return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
		}
	}
	return nil
}

// copyFile copies src to dst relative to dstRoot.
func copyFile(dstRoot, dst, src string) error {
	s, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("unable to open %v: %v", src, err)
	}
	defer s.Close()

	dst = filepath.Join(dstRoot, dst)
	d, err := os.Create(dst)
	if err != nil {
		return fmt.Errorf("unable to create %v: %v", dst)
	}
	_, err = io.Copy(d, s)
	if err != nil {
		d.Close() // ignore error, copy already failed.
		return fmt.Errorf("unable to copy %v to %v: %v", src, dst, err)
	}
	if err := d.Close(); err != nil {
		return fmt.Errorf("unable to close %v: %v", dst, err)
	}
	return nil
}

// appFiles returns a list of all Go source files in the app.
func appFiles(ctxt *build.Context) ([]string, error) {
	pkg, err := ctxt.ImportDir(".", 0)
	if err != nil {
		return nil, err
	}
	if !pkg.IsCommand() {
		return nil, fmt.Errorf(`the root of your app needs to be package "main" (currently %q). Please see https://cloud.google.com/appengine/docs/go/managed-vms for more details on structuring your app.`, pkg.Name)
	}
	var appFiles []string
	for _, f := range pkg.GoFiles {
		n := filepath.Join(".", f)
		appFiles = append(appFiles, n)
	}
	return appFiles, nil
}
<commit_msg>appengine/cmd/aedeploy: follow symlinks to folders when copying tree<commit_after>// Copyright 2015 Google Inc. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

// Program aedeploy assists with deploying Go Managed VM apps to production.
// A temporary directory is created; the app, its subdirectories, and all its
// dependencies from $GOPATH are copied into the directory; then the app
// is deployed to production with the provided command.
//
// The app must be in "package main".
//
// This command must be issued from within the root directory of the app
// (where the app.yaml file is located).
package main

import (
	"flag"
	"fmt"
	"go/build"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	skipFiles = map[string]bool{
		".git":        true,
		".gitconfig":  true,
		".hg":         true,
		".travis.yml": true,
	}

	gopathCache = map[string]string{}
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	fmt.Fprintf(os.Stderr, "\t%s gcloud --verbosity debug preview app deploy --version myversion ./app.yaml\tDeploy app to production\n", os.Args[0])
}

func main() {
	flag.Usage = usage
	flag.Parse()
	if flag.NArg() < 1 {
		usage()
		os.Exit(1)
	}

	if err := aedeploy(); err != nil {
		fmt.Fprintf(os.Stderr, os.Args[0]+": Error: %v\n", err)
		os.Exit(1)
	}
}

func aedeploy() error {
	tags := []string{"appenginevm"}
	app, err := analyze(tags)
	if err != nil {
		return err
	}

	tmpDir, err := app.bundle()
	if tmpDir != "" {
		defer os.RemoveAll(tmpDir)
	}
	if err != nil {
		return err
	}

	if err := os.Chdir(tmpDir); err != nil {
		return fmt.Errorf("unable to chdir to %v: %v", tmpDir, err)
	}
	return deploy()
}

// deploy calls the provided command to deploy the app from the temporary directory.
func deploy() error {
	cmd := exec.Command(flag.Arg(0), flag.Args()[1:]...)
	cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("unable to run %q: %v", strings.Join(flag.Args(), " "), err)
	}
	return nil
}

type app struct {
	appFiles []string
	imports  map[string]string
}

// analyze checks the app for building with the given build tags and returns
// app files, and a map of full directory import names to original import names.
func analyze(tags []string) (*app, error) {
	ctxt := buildContext(tags)
	appFiles, err := appFiles(ctxt)
	if err != nil {
		return nil, err
	}
	gopath := filepath.SplitList(ctxt.GOPATH)
	im, err := imports(ctxt, ".", gopath)
	return &app{
		appFiles: appFiles,
		imports:  im,
	}, err
}

// buildContext returns the context for building the source.
func buildContext(tags []string) *build.Context {
	return &build.Context{
		GOARCH:    "amd64",
		GOOS:      "linux",
		GOROOT:    build.Default.GOROOT,
		GOPATH:    build.Default.GOPATH,
		Compiler:  build.Default.Compiler,
		BuildTags: append(build.Default.BuildTags, tags...),
	}
}

// bundle bundles the app into a temporary directory.
func (s *app) bundle() (tmpdir string, err error) {
	workDir, err := ioutil.TempDir("", "aedeploy")
	if err != nil {
		return "", fmt.Errorf("unable to create tmpdir: %v", err)
	}

	for srcDir, importName := range s.imports {
		dstDir := "_gopath/src/" + importName
		if err := copyTree(workDir, dstDir, srcDir); err != nil {
			return workDir, fmt.Errorf("unable to copy directory %v to %v: %v", srcDir, dstDir, err)
		}
	}
	if err := copyTree(workDir, ".", "."); err != nil {
		return workDir, fmt.Errorf("unable to copy root directory to /app: %v", err)
	}
	return workDir, nil
}

// imports returns a map of all import directories (recursively) used by the app.
// The return value maps full directory names to original import names.
func imports(ctxt *build.Context, srcDir string, gopath []string) (map[string]string, error) {
	pkg, err := ctxt.ImportDir(srcDir, 0)
	if err != nil {
		return nil, err
	}

	// Resolve all non-standard-library imports
	result := make(map[string]string)
	for _, v := range pkg.Imports {
		if !strings.Contains(v, ".") {
			continue
		}
		src, err := findInGopath(v, gopath)
		if err != nil {
			return nil, fmt.Errorf("unable to find import %v in gopath %v: %v", v, gopath, err)
		}
		if _, ok := result[src]; ok { // Already processed
			continue
		}
		result[src] = v
		im, err := imports(ctxt, src, gopath)
		if err != nil {
			return nil, fmt.Errorf("unable to parse package %v: %v", src, err)
		}
		for k, v := range im {
			result[k] = v
		}
	}
	return result, nil
}

// findInGopath searches the gopath for the named import directory.
func findInGopath(dir string, gopath []string) (string, error) {
	if v, ok := gopathCache[dir]; ok {
		return v, nil
	}
	for _, v := range gopath {
		dst := filepath.Join(v, "src", dir)
		if _, err := os.Stat(dst); err == nil {
			gopathCache[dir] = dst
			return dst, nil
		}
	}
	return "", fmt.Errorf("unable to find package %v in gopath %v", dir, gopath)
}

// copyTree copies srcDir to dstDir relative to dstRoot, ignoring skipFiles.
func copyTree(dstRoot, dstDir, srcDir string) error {
	d := filepath.Join(dstRoot, dstDir)
	if err := os.MkdirAll(d, 0755); err != nil {
		return fmt.Errorf("unable to create directory %v: %v", d, err)
	}

	entries, err := ioutil.ReadDir(srcDir)
	if err != nil {
		return fmt.Errorf("unable to read dir %v: %v", srcDir, err)
	}
	for _, entry := range entries {
		n := entry.Name()
		if skipFiles[n] {
			continue
		}
		s := filepath.Join(srcDir, n)
		if entry.Mode()&os.ModeSymlink == os.ModeSymlink {
			if entry, err = os.Stat(s); err != nil {
				return fmt.Errorf("unable to stat %v: %v", s, err)
			}
		}
		d := filepath.Join(dstDir, n)
		if entry.IsDir() {
			if err := copyTree(dstRoot, d, s); err != nil {
				return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
			}
			continue
		}
		if err := copyFile(dstRoot, d, s); err != nil {
			return fmt.Errorf("unable to copy dir %v to %v: %v", s, d, err)
		}
	}
	return nil
}

// copyFile copies src to dst relative to dstRoot.
func copyFile(dstRoot, dst, src string) error {
	s, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("unable to open %v: %v", src, err)
	}
	defer s.Close()

	dst = filepath.Join(dstRoot, dst)
	d, err := os.Create(dst)
	if err != nil {
		return fmt.Errorf("unable to create %v: %v", dst)
	}
	_, err = io.Copy(d, s)
	if err != nil {
		d.Close() // ignore error, copy already failed.
		return fmt.Errorf("unable to copy %v to %v: %v", src, dst, err)
	}
	if err := d.Close(); err != nil {
		return fmt.Errorf("unable to close %v: %v", dst, err)
	}
	return nil
}

// appFiles returns a list of all Go source files in the app.
func appFiles(ctxt *build.Context) ([]string, error) {
	pkg, err := ctxt.ImportDir(".", 0)
	if err != nil {
		return nil, err
	}
	if !pkg.IsCommand() {
		return nil, fmt.Errorf(`the root of your app needs to be package "main" (currently %q). Please see https://cloud.google.com/appengine/docs/go/managed-vms for more details on structuring your app.`, pkg.Name)
	}
	var appFiles []string
	for _, f := range pkg.GoFiles {
		n := filepath.Join(".", f)
		appFiles = append(appFiles, n)
	}
	return appFiles, nil
}
<|endoftext|>


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

package main

import (
	"bufio"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/dep"
	fb "github.com/golang/dep/internal/feedback"
	"github.com/golang/dep/internal/gps"
	"github.com/pkg/errors"
)

func vndrFile(dir string) string {
	return filepath.Join(dir, "vendor.conf")
}

type vndrImporter struct {
	packages []vndrPackage

	logger  *log.Logger
	verbose bool
	sm      gps.SourceManager
}

func newVndrImporter(log *log.Logger, verbose bool, sm gps.SourceManager) *vndrImporter {
	return &vndrImporter{
		logger:  log,
		verbose: verbose,
		sm:      sm,
	}
}

func (v *vndrImporter) Name() string { return "vndr" }

func (v *vndrImporter) HasDepMetadata(dir string) bool {
	_, err := os.Stat(vndrFile(dir))
	return err == nil
}

func (v *vndrImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
	v.logger.Println("Detected vndr configuration file...")

	err := v.loadVndrFile(dir)
	if err != nil {
		return nil, nil, errors.Wrapf(err, "unable to load vndr file")
	}

	return v.convert(pr)
}

func (v *vndrImporter) loadVndrFile(dir string) error {
	v.logger.Printf("Converting from vendor.conf...")

	f, err := os.Open(vndrFile(dir))
	if err != nil {
		return errors.Wrapf(err, "Unable to open %s", vndrFile(dir))
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		pkg, err := parseVndrLine(scanner.Text())
		if err != nil {
			return errors.Wrapf(err, "unable to parse line")
		}
		if pkg == nil {
			// Could be an empty line or one which is just a comment
			continue
		}
		v.packages = append(v.packages, *pkg)
	}

	if scanner.Err() != nil {
		return errors.Wrapf(err, "unable to read %s", vndrFile(dir))
	}

	return nil
}

func (v *vndrImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
	var (
		manifest = dep.NewManifest()
		lock     = &dep.Lock{}
		err      error
	)

	for _, pkg := range v.packages {
		pc := gps.ProjectConstraint{
			Ident: gps.ProjectIdentifier{
				ProjectRoot: gps.ProjectRoot(pkg.importPath),
				Source:      pkg.repository,
			},
		}
		pc.Constraint, err = v.sm.InferConstraint(pkg.revision, pc.Ident)
		if err != nil {
			return nil, nil, errors.Wrapf(err, "Unable to interpret revision specifier '%s' for package %s", pkg.importPath, pkg.revision)
		}

		manifest.Constraints[pc.Ident.ProjectRoot] = gps.ProjectProperties{
			Source:     pc.Ident.Source,
			Constraint: pc.Constraint,
		}
		fb.NewConstraintFeedback(pc, fb.DepTypeImported).LogFeedback(v.logger)

		revision := gps.Revision(pkg.revision)
		version, err := lookupVersionForLockedProject(pc.Ident, pc.Constraint, revision, v.sm)
		if err != nil {
			v.logger.Println(err.Error())
		}

		lp := gps.NewLockedProject(pc.Ident, version, nil)

		lock.P = append(lock.P, lp)
		fb.NewLockedProjectFeedback(lp, fb.DepTypeImported).LogFeedback(v.logger)
	}

	return manifest, lock, nil
}

type vndrPackage struct {
	importPath string
	revision   string
	repository string
}

func parseVndrLine(line string) (*vndrPackage, error) {
	commentIdx := strings.Index(line, "#")
	if commentIdx >= 0 {
		line = line[:commentIdx]
	}
	line = strings.TrimSpace(line)

	if line == "" {
		return nil, nil
	}

	parts := strings.Fields(line)

	if !(len(parts) == 2 || len(parts) == 3) {
		return nil, errors.Errorf("invalid config format: %q", line)
	}

	pkg := &vndrPackage{
		importPath: parts[0],
		revision:   parts[1],
	}
	if len(parts) == 3 {
		pkg.repository = parts[2]
	}

	return pkg, nil
}
<commit_msg>cmd/dep: fix vndr importpath validation<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bufio"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/golang/dep"
	fb "github.com/golang/dep/internal/feedback"
	"github.com/golang/dep/internal/gps"
	"github.com/pkg/errors"
)

func vndrFile(dir string) string {
	return filepath.Join(dir, "vendor.conf")
}

type vndrImporter struct {
	packages []vndrPackage

	logger  *log.Logger
	verbose bool
	sm      gps.SourceManager
}

func newVndrImporter(log *log.Logger, verbose bool, sm gps.SourceManager) *vndrImporter {
	return &vndrImporter{
		logger:  log,
		verbose: verbose,
		sm:      sm,
	}
}

func (v *vndrImporter) Name() string { return "vndr" }

func (v *vndrImporter) HasDepMetadata(dir string) bool {
	_, err := os.Stat(vndrFile(dir))
	return err == nil
}

func (v *vndrImporter) Import(dir string, pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
	v.logger.Println("Detected vndr configuration file...")

	err := v.loadVndrFile(dir)
	if err != nil {
		return nil, nil, errors.Wrapf(err, "unable to load vndr file")
	}

	return v.convert(pr)
}

func (v *vndrImporter) loadVndrFile(dir string) error {
	v.logger.Printf("Converting from vendor.conf...")

	f, err := os.Open(vndrFile(dir))
	if err != nil {
		return errors.Wrapf(err, "Unable to open %s", vndrFile(dir))
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		pkg, err := parseVndrLine(scanner.Text())
		if err != nil {
			return errors.Wrapf(err, "unable to parse line")
		}
		if pkg == nil {
			// Could be an empty line or one which is just a comment
			continue
		}
		v.packages = append(v.packages, *pkg)
	}

	if scanner.Err() != nil {
		return errors.Wrapf(err, "unable to read %s", vndrFile(dir))
	}

	return nil
}

func (v *vndrImporter) convert(pr gps.ProjectRoot) (*dep.Manifest, *dep.Lock, error) {
	var (
		manifest = dep.NewManifest()
		lock     = &dep.Lock{}
		err      error
	)

	for _, pkg := range v.packages {
		// ImportPath must not be empty
		if pkg.importPath == "" {
			err := errors.New("Invalid vndr configuration, missing import path")
			return nil, nil, err
		}

		pc := gps.ProjectConstraint{
			Ident: gps.ProjectIdentifier{
				ProjectRoot: gps.ProjectRoot(pkg.importPath),
				Source:      pkg.repository,
			},
		}
		pc.Constraint, err = v.sm.InferConstraint(pkg.revision, pc.Ident)
		if err != nil {
			return nil, nil, errors.Wrapf(err, "Unable to interpret revision specifier '%s' for package %s", pkg.importPath, pkg.revision)
		}

		manifest.Constraints[pc.Ident.ProjectRoot] = gps.ProjectProperties{
			Source:     pc.Ident.Source,
			Constraint: pc.Constraint,
		}
		fb.NewConstraintFeedback(pc, fb.DepTypeImported).LogFeedback(v.logger)

		revision := gps.Revision(pkg.revision)
		version, err := lookupVersionForLockedProject(pc.Ident, pc.Constraint, revision, v.sm)
		if err != nil {
			v.logger.Println(err.Error())
		}

		lp := gps.NewLockedProject(pc.Ident, version, nil)

		lock.P = append(lock.P, lp)
		fb.NewLockedProjectFeedback(lp, fb.DepTypeImported).LogFeedback(v.logger)
	}

	return manifest, lock, nil
}

type vndrPackage struct {
	importPath string
	revision   string
	repository string
}

func parseVndrLine(line string) (*vndrPackage, error) {
	commentIdx := strings.Index(line, "#")
	if commentIdx >= 0 {
		line = line[:commentIdx]
	}
	line = strings.TrimSpace(line)

	if line == "" {
		return nil, nil
	}

	parts := strings.Fields(line)

	if !(len(parts) == 2 || len(parts) == 3) {
		return nil, errors.Errorf("invalid config format: %q", line)
	}

	pkg := &vndrPackage{
		importPath: parts[0],
		revision:   parts[1],
	}
	if len(parts) == 3 {
		pkg.repository = parts[2]
	}

	return pkg, nil
}
<|endoftext|>