<commit_before>package regression

import (
	"bytes"
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.skia.org/infra/go/paramtools"
	"go.skia.org/infra/go/testutils/unittest"
	"go.skia.org/infra/go/vec32"
	"go.skia.org/infra/perf/go/alerts"
	"go.skia.org/infra/perf/go/dataframe/mocks"
	"go.skia.org/infra/perf/go/progress"
	"go.skia.org/infra/perf/go/types"
)

const (
	e = vec32.MissingDataSentinel
)

func TestTooMuchMissingData(t *testing.T) {
	unittest.SmallTest(t)
	testCases := []struct {
		value    types.Trace
		expected bool
		message  string
	}{
		{
			value:    types.Trace{e, e, 1, 1, 1},
			expected: true,
			message:  "missing one side",
		},
		{
			value:    types.Trace{1, e, 1, 1, 1},
			expected: false,
			message:  "exactly 50%",
		},
		{
			value:    types.Trace{1, 1, e, 1, 1},
			expected: true,
			message:  "missing midpoint",
		},
		{
			value:    types.Trace{e, e, 1, 1},
			expected: true,
			message:  "missing one side - even",
		},
		{
			value:    types.Trace{e, 1, 1, 1},
			expected: false,
			message:  "exactly 50% - even",
		},
		{
			value:    types.Trace{e, 1, 1},
			expected: true,
			message:  "Radius = 1",
		},
		{
			value:    types.Trace{1},
			expected: false,
			message:  "len(tr) < 3",
		},
	}

	for _, tc := range testCases {
		if got, want := tooMuchMissingData(tc.value), tc.expected; got != want {
			t.Errorf("Failed case Got %v Want %v: %s", got, want, tc.message)
		}
	}
}

func TestProcessRegressions_BadQueryValue_ReturnsNil(t *testing.T) {
	unittest.SmallTest(t)

	req := &RegressionDetectionRequest{
		Query:    "http://[::1]a", // A known query that will fail to parse.
		Progress: progress.New(),
		Alert:    alerts.NewConfig(),
	}

	dfb := &mocks.DataFrameBuilder{}
	err := ProcessRegressions(context.Background(), req, nil, nil, nil, dfb, paramtools.NewReadOnlyParamSet())
	require.NoError(t, err)
	assert.Equal(t, progress.Running, req.Progress.Status())
	var b bytes.Buffer
	err = req.Progress.JSON(&b)
	require.NoError(t, err)
	assert.Equal(t, "{\"status\":\"Running\",\"messages\":[{\"key\":\"Stage\",\"value\":\"Loading data to analyze\"}],\"url\":\"\"}\n", b.String())
}

func TestAllRequestsFromBaseRequest_WithValidGroupBy_Success(t *testing.T) {
	unittest.SmallTest(t)

	baseRequest := NewRegressionDetectionRequest()
	alert := alerts.NewConfig()
	alert.GroupBy = "config"
	alert.Query = "arch=x86"
	baseRequest.Alert = alert
	ps := paramtools.ReadOnlyParamSet{
		"config": []string{"8888", "565"},
		"arch":   []string{"x86", "arm"},
	}
	allRequests := allRequestsFromBaseRequest(baseRequest, ps)
	assert.Len(t, allRequests, 2)
	assert.Contains(t, []string{"arch=x86&config=8888", "arch=x86&config=565"}, allRequests[0].Query)
}

func TestAllRequestsFromBaseRequest_WithInvalidGroupBy_NoRequestsReturned(t *testing.T) {
	unittest.SmallTest(t)

	baseRequest := NewRegressionDetectionRequest()
	alert := alerts.NewConfig()
	alert.GroupBy = "SomeUnknownKey"
	alert.Query = "arch=x86"
	baseRequest.Alert = alert
	ps := paramtools.ReadOnlyParamSet{
		"config": []string{"8888", "565"},
		"arch":   []string{"x86", "arm"},
	}
	allRequests := allRequestsFromBaseRequest(baseRequest, ps)
	assert.Empty(t, allRequests)
}

func TestAllRequestsFromBaseRequest_WithoutGroupBy_BaseRequestReturnedUnchanged(t *testing.T) {
	unittest.SmallTest(t)

	baseRequest := NewRegressionDetectionRequest()
	alert := alerts.NewConfig()
	alert.GroupBy = ""
	alert.Query = "arch=x86"
	baseRequest.Alert = alert
	ps := paramtools.ReadOnlyParamSet{
		"config": []string{"8888", "565"},
		"arch":   []string{"x86", "arm"},
	}
	allRequests := allRequestsFromBaseRequest(baseRequest, ps)
	// With no GroupBy a slice with just the baseRequest is returned.
	assert.Len(t, allRequests, 1)
	// Intentionally comparing pointers.
	assert.Equal(t, baseRequest, allRequests[0])
}
<commit_msg>[perf] Quick fix for breaking test.<commit_after>package regression

import (
	"bytes"
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.skia.org/infra/go/paramtools"
	"go.skia.org/infra/go/testutils/unittest"
	"go.skia.org/infra/go/vec32"
	"go.skia.org/infra/perf/go/alerts"
	"go.skia.org/infra/perf/go/dataframe/mocks"
	"go.skia.org/infra/perf/go/progress"
	"go.skia.org/infra/perf/go/types"
)

const (
	e = vec32.MissingDataSentinel
)

func TestTooMuchMissingData(t *testing.T) {
	unittest.SmallTest(t)
	testCases := []struct {
		value    types.Trace
		expected bool
		message  string
	}{
		{
			value:    types.Trace{e, e, 1, 1, 1},
			expected: true,
			message:  "missing one side",
		},
		{
			value:    types.Trace{1, e, 1, 1, 1},
			expected: false,
			message:  "exactly 50%",
		},
		{
			value:    types.Trace{1, 1, e, 1, 1},
			expected: true,
			message:  "missing midpoint",
		},
		{
			value:    types.Trace{e, e, 1, 1},
			expected: true,
			message:  "missing one side - even",
		},
		{
			value:    types.Trace{e, 1, 1, 1},
			expected: false,
			message:  "exactly 50% - even",
		},
		{
			value:    types.Trace{e, 1, 1},
			expected: true,
			message:  "Radius = 1",
		},
		{
			value:    types.Trace{1},
			expected: false,
			message:  "len(tr) < 3",
		},
	}

	for _, tc := range testCases {
		if got, want := tooMuchMissingData(tc.value), tc.expected; got != want {
			t.Errorf("Failed case Got %v Want %v: %s", got, want, tc.message)
		}
	}
}

func TestProcessRegressions_BadQueryValue_ReturnsNil(t *testing.T) {
	unittest.SmallTest(t)

	alert := alerts.NewConfig() // A known query that will fail to parse.
	alert.Query = "http://[::1]a"
	req := &RegressionDetectionRequest{
		Progress: progress.New(),
		Alert:    alert,
	}

	dfb := &mocks.DataFrameBuilder{}
	err := ProcessRegressions(context.Background(), req, nil, nil, nil, dfb, paramtools.NewReadOnlyParamSet())
	require.NoError(t, err)
	assert.Equal(t, progress.Running, req.Progress.Status())
	var b bytes.Buffer
	err = req.Progress.JSON(&b)
	require.NoError(t, err)
	assert.Equal(t, "{\"status\":\"Running\",\"messages\":[{\"key\":\"Stage\",\"value\":\"Loading data to analyze\"}],\"url\":\"\"}\n", b.String())
}

func TestAllRequestsFromBaseRequest_WithValidGroupBy_Success(t *testing.T) {
	unittest.SmallTest(t)

	baseRequest := NewRegressionDetectionRequest()
	alert := alerts.NewConfig()
	alert.GroupBy = "config"
	alert.Query = "arch=x86"
	baseRequest.Alert = alert
	ps := paramtools.ReadOnlyParamSet{
		"config": []string{"8888", "565"},
		"arch":   []string{"x86", "arm"},
	}
	allRequests := allRequestsFromBaseRequest(baseRequest, ps)
	assert.Len(t, allRequests, 2)
	assert.Contains(t, []string{"arch=x86&config=8888", "arch=x86&config=565"}, allRequests[0].Query)
}

func TestAllRequestsFromBaseRequest_WithInvalidGroupBy_NoRequestsReturned(t *testing.T) {
	unittest.SmallTest(t)

	baseRequest := NewRegressionDetectionRequest()
	alert := alerts.NewConfig()
	alert.GroupBy = "SomeUnknownKey"
	alert.Query = "arch=x86"
	baseRequest.Alert = alert
	ps := paramtools.ReadOnlyParamSet{
		"config": []string{"8888", "565"},
		"arch":   []string{"x86", "arm"},
	}
	allRequests := allRequestsFromBaseRequest(baseRequest, ps)
	assert.Empty(t, allRequests)
}

func TestAllRequestsFromBaseRequest_WithoutGroupBy_BaseRequestReturnedUnchanged(t *testing.T) {
	unittest.SmallTest(t)

	baseRequest := NewRegressionDetectionRequest()
	alert := alerts.NewConfig()
	alert.GroupBy = ""
	alert.Query = "arch=x86"
	baseRequest.Alert = alert
	ps := paramtools.ReadOnlyParamSet{
		"config": []string{"8888", "565"},
		"arch":   []string{"x86", "arm"},
	}
	allRequests := allRequestsFromBaseRequest(baseRequest, ps)
	// With no GroupBy a slice with just the baseRequest is returned.
	assert.Len(t, allRequests, 1)
	// Intentionally comparing pointers.
	assert.Equal(t, baseRequest, allRequests[0])
}
<|endoftext|>


<commit_before>package file

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"github.com/fatih/color"
	"github.com/nomad-software/replace/cli"
)

type Handler struct {
	Options *cli.Options
	Group   sync.WaitGroup
	Output  *cli.Output
}

func (this *Handler) Init(options *cli.Options) {
	this.Options = options
	this.Output = &cli.Output{
		make(chan string),
		make(chan bool),
	}
}

func (this *Handler) handlePath(fullPath string) {
	defer this.Group.Done()

	matched, err := filepath.Match(this.Options.File, path.Base(fullPath))
	if err != nil {
		fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
		return
	}

	if matched {
		this.Group.Add(1)
		go this.processPath(fullPath)
	}
}

func (this *Handler) processPath(fullPath string) {
	defer this.Group.Done()

	contents, err := ioutil.ReadFile(fullPath)
	if err != nil {
		fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
		return
	}

	newContents := strings.Replace(string(contents), this.Options.From, this.Options.To, -1)
	if newContents != string(contents) {

		err = ioutil.WriteFile(fullPath, []byte(newContents), 0)
		if err != nil {
			fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
			return
		}

		this.Output.Console <- fullPath
	}
}

func (this *Handler) Walk() error {
	return filepath.Walk(this.Options.Dir, func(fullPath string, info os.FileInfo, err error) error {

		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		this.Group.Add(1)
		go this.handlePath(fullPath)

		return nil
	})
}
<commit_msg>Fixed go vet issues.<commit_after>package file

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"github.com/fatih/color"
	"github.com/nomad-software/replace/cli"
)

type Handler struct {
	Options *cli.Options
	Group   sync.WaitGroup
	Output  *cli.Output
}

func (this *Handler) Init(options *cli.Options) {
	this.Options = options
	this.Output = &cli.Output{
		Console: make(chan string),
		Closed:  make(chan bool),
	}
}

func (this *Handler) handlePath(fullPath string) {
	defer this.Group.Done()

	matched, err := filepath.Match(this.Options.File, path.Base(fullPath))
	if err != nil {
		fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
		return
	}

	if matched {
		this.Group.Add(1)
		go this.processPath(fullPath)
	}
}

func (this *Handler) processPath(fullPath string) {
	defer this.Group.Done()

	contents, err := ioutil.ReadFile(fullPath)
	if err != nil {
		fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
		return
	}

	newContents := strings.Replace(string(contents), this.Options.From, this.Options.To, -1)
	if newContents != string(contents) {

		err = ioutil.WriteFile(fullPath, []byte(newContents), 0)
		if err != nil {
			fmt.Fprintln(os.Stderr, color.RedString(err.Error()))
			return
		}

		this.Output.Console <- fullPath
	}
}

func (this *Handler) Walk() error {
	return filepath.Walk(this.Options.Dir, func(fullPath string, info os.FileInfo, err error) error {

		if err != nil {
			return err
		}

		if info.IsDir() {
			return nil
		}

		this.Group.Add(1)
		go this.handlePath(fullPath)

		return nil
	})
}
<|endoftext|>


<commit_before>package images

import (
	"Fantasia/system"
	"image"
	"image/gif"
	"image/png"
	"io"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"

	"github.com/bwmarrin/discordgo"
)

// util.go : provides various utility functions
var urlRegex = regexp.MustCompile(`(http|ftp|https):\/\/([\w\-_]+(?:(?:\.[\w\-_]+)+))([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?`)

// ImageSuffixes is a list of image suffixes
var ImageSuffixes = []string{
	"png",
	"jpeg",
	"jpg",
	"gif",
}

// ReplyImage replies to the sender with the given image
func ReplyImage(ctx *system.Context, img image.Image) {
	rd, wr := io.Pipe()
	go func() {
		png.Encode(wr, img)
		wr.Close()
	}()
	ctx.Ses.DG.ChannelFileSend(ctx.Msg.ChannelID, "image.png", rd)
}

// ReplyGif replies to the sender with the given gif
func ReplyGif(ctx *system.Context, g *gif.GIF) {
	rd, wr := io.Pipe()
	go func() {
		err := gif.EncodeAll(wr, g)
		if err != nil {
			ctx.ReplyError("error encoding gif: ", err)
		}
		wr.Close()
	}()
	_, err := ctx.Ses.DG.ChannelFileSend(ctx.Msg.ChannelID, "image.gif", rd)
	if err != nil {
		ctx.ReplyError("Error uploading gif: ", err)
	}
}

// CompressGif attempts to compress a Gif's images
func CompressGif(src *gif.GIF) *gif.GIF {
	dst := &gif.GIF{
		Image:    make([]*image.Paletted, len(src.Image)),
		Disposal: make([]byte, len(src.Disposal)),
	}
	// Set disposal to none.
	for i := 0; i < len(dst.Disposal); i++ {
		dst.Disposal[i] = gif.DisposalNone
	}

	return dst
}

// PullImages requests images from the user, or retrieves them from the cache.
//    amount    : amount of messages to retrieve
//    channelID : channelID to pull images from
//    message   : message user supplied. If images are present, pull them from here first.
//                otherwise fall back to the image cache.
func (m *Module) PullImages(limit int, channelID string, message *discordgo.Message) ([]image.Image, error) {
	images := make([]image.Image, 0, limit)

	// Pull images from message issuing command.
	tmp, err := PullImagesFromMessage(limit, message)
	if err != nil {
		return images, err
	}
	images = append(images, tmp...)

	// If we have satisfied the message limit, return
	if len(images) >= limit {
		return images, nil
	}

	// Else continue searching the cache for images
	tmp, err = PullImagesFromCache(m.ImgCache, limit-len(images), channelID)
	if err != nil {
		return images, err
	}
	images = append(images, tmp...)

	return images, nil
}

// PullImagesFromMessage retrieves images from a message
func PullImagesFromMessage(limit int, msg *discordgo.Message) ([]image.Image, error) {
	URLs := ImageURLsInMessage(msg)
	images := []image.Image{}

	for _, v := range URLs {
		img, err := ImageFromURL(v)
		if err != nil {
			return images, err
		}
		images = append(images, img)

		limit--
		if limit == 0 {
			break
		}
	}

	return images, nil
}

// ImageFromURL gets an image from a URL
//    URL : URL to perform an HTTP get reqeust to
func ImageFromURL(URL string) (image.Image, error) {
	resp, err := http.Get(URL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	img, _, err := image.Decode(resp.Body)
	return img, err
}

// ImageURLsInMessage returns a list of image URLs in a message
// Images are obtained from embed images, and thumbnails, and attachments.
//    msg : message to obtain the URLs from
func ImageURLsInMessage(msg *discordgo.Message) []string {
	URLs := []string{}

	for _, a := range msg.Attachments {
		URLs = append(URLs, a.URL)
	}

	for _, embed := range msg.Embeds {
		if embed.Image != nil && embed.Image.ProxyURL != "" {
			URLs = append(URLs, embed.Image.ProxyURL)
		}
		if embed.Thumbnail != nil && embed.Thumbnail.ProxyURL != "" {
			URLs = append(URLs, embed.Thumbnail.ProxyURL)
		}
	}

	if len(msg.Embeds) == 0 {
		for _, u := range urlRegex.FindAllString(msg.Content, -1) {
			if IsImageURL(u) {
				URLs = append(URLs, u)
			}
		}
	}

	return URLs
}

// IsImageURL guesses if a URL is an image
func IsImageURL(path string) bool {
	t, err := url.Parse(path)
	if err != nil {
		log.Println("error parsing URL")
	} else {
		if HasImageSuffix(strings.ToLower(t.Path)) {
			return true
		}
	}
	return false
}

// PullImagesFromCache ...
func PullImagesFromCache(cache *MessageCache, limit int, channelID string) ([]image.Image, error) {
	images := make([]image.Image, 0, limit)

	messages, err := cache.Messages(channelID)
	if err != nil {
		return nil, err
	}

	for i := len(messages) - 1; i >= 0; i-- {
		v := messages[i]
		tmp, err := PullImagesFromMessage(limit, v)
		if err != nil {
			return nil, err
		}
		images = append(images, tmp...)

		// Decrement the image limit
		limit -= len(images)
		if limit <= 0 {
			break
		}
	}

	return images, nil
}

// HasImage returns if a message has an image
func HasImage(msg *discordgo.Message) bool {

	for _, embed := range msg.Embeds {
		if embed.Image != nil && embed.Image.URL != "" {
			return true
		}
		if embed.Thumbnail != nil && embed.Thumbnail.URL != "" {
			return true
		}
	}

	for _, v := range msg.Attachments {
		if HasImageSuffix(v.Filename) {
			return true
		}
	}

	for _, u := range urlRegex.FindAllString(msg.Content, -1) {
		if IsImageURL(u) {
			return true
		}
	}

	return false
}

// HasImageSuffix returns true if an image suffix is present
func HasImageSuffix(filename string) bool {
	for _, v := range ImageSuffixes {
		if strings.HasSuffix(filename, v) {
			return true
		}
	}
	return false
}
<commit_msg>CompressGif now works<commit_after>package images

import (
	"Fantasia/system"
	"image"
	"image/color"
	"image/gif"
	"image/png"
	"io"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"

	"github.com/bwmarrin/discordgo"
)

// util.go : provides various utility functions
var urlRegex = regexp.MustCompile(`(http|ftp|https):\/\/([\w\-_]+(?:(?:\.[\w\-_]+)+))([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?`)

// ImageSuffixes is a list of image suffixes
var ImageSuffixes = []string{
	"png",
	"jpeg",
	"jpg",
	"gif",
}

// ReplyImage replies to the sender with the given image
func ReplyImage(ctx *system.Context, img image.Image) {
	rd, wr := io.Pipe()
	go func() {
		png.Encode(wr, img)
		wr.Close()
	}()
	ctx.Ses.DG.ChannelFileSend(ctx.Msg.ChannelID, "image.png", rd)
}

// ReplyGif replies to the sender with the given gif
func ReplyGif(ctx *system.Context, g *gif.GIF) {
	rd, wr := io.Pipe()
	go func() {
		err := gif.EncodeAll(wr, g)
		if err != nil {
			ctx.ReplyError("error encoding gif: ", err)
		}
		wr.Close()
	}()
	_, err := ctx.Ses.DG.ChannelFileSend(ctx.Msg.ChannelID, "image.gif", rd)
	if err != nil {
		ctx.ReplyError("Error uploading gif: ", err)
	}
}

// CompressGif attempts to compress a Gif's images
func CompressGif(src *gif.GIF) *gif.GIF {
	dst := &gif.GIF{
		Image:    make([]*image.Paletted, len(src.Image)),
		Disposal: make([]byte, len(src.Disposal)),
		Delay:    src.Delay,
	}
	// Set disposal to none.
	for i := 0; i < len(dst.Disposal); i++ {
		dst.Disposal[i] = 1
	}

	diff := func(a, b uint32) bool {
		if a >= b-500 && a <= b+500 {
			return false
		}
		return true
	}

	var frame = src.Image[0]
	dst.Image[0] = frame
	for i := 1; i < len(src.Image); i++ {
		cp := src.Image[i]
		np := image.NewPaletted(src.Image[i].Bounds(), cp.Palette)
		np.Palette[0] = color.RGBA{0, 0, 0, 0} // Insert an alpha pixel into the palette

		w, h := cp.Bounds().Dx(), cp.Bounds().Dy()
		for y := 0; y < h; y++ {
			for x := 0; x < w; x++ {
				pidx := y*cp.Stride + x*1
				// Compare the colours of frame(last image) and cp(current paletted),
				// if they are different, add the pixel to new paletted (np)
				r, g, b, a := cp.Palette[cp.Pix[pidx]].RGBA()
				r1, g1, b1, a1 := frame.Palette[frame.Pix[pidx]].RGBA()

				if diff(r, r1) || diff(g, g1) || diff(b, b1) || diff(a, a1) {
					np.Pix[pidx] = cp.Pix[pidx]
				} else {
					np.Pix[pidx] = uint8(np.Palette.Index(color.RGBA{0, 0, 0, 0}))
				}
			}
		}

		dst.Image[i] = np
		frame = cp // Update last frame
	}

	return dst
}

// PullImages requests images from the user, or retrieves them from the cache.
//    amount    : amount of messages to retrieve
//    channelID : channelID to pull images from
//    message   : message user supplied. If images are present, pull them from here first.
//                otherwise fall back to the image cache.
func (m *Module) PullImages(limit int, channelID string, message *discordgo.Message) ([]image.Image, error) {
	images := make([]image.Image, 0, limit)

	// Pull images from message issuing command.
	tmp, err := PullImagesFromMessage(limit, message)
	if err != nil {
		return images, err
	}
	images = append(images, tmp...)

	// If we have satisfied the message limit, return
	if len(images) >= limit {
		return images, nil
	}

	// Else continue searching the cache for images
	tmp, err = PullImagesFromCache(m.ImgCache, limit-len(images), channelID)
	if err != nil {
		return images, err
	}
	images = append(images, tmp...)

	return images, nil
}

// PullImagesFromMessage retrieves images from a message
func PullImagesFromMessage(limit int, msg *discordgo.Message) ([]image.Image, error) {
	URLs := ImageURLsInMessage(msg)
	images := []image.Image{}

	for _, v := range URLs {
		img, err := ImageFromURL(v)
		if err != nil {
			return images, err
		}
		images = append(images, img)

		limit--
		if limit == 0 {
			break
		}
	}

	return images, nil
}

// ImageFromURL gets an image from a URL
//    URL : URL to perform an HTTP get reqeust to
func ImageFromURL(URL string) (image.Image, error) {
	resp, err := http.Get(URL)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	img, _, err := image.Decode(resp.Body)
	return img, err
}

// ImageURLsInMessage returns a list of image URLs in a message
// Images are obtained from embed images, and thumbnails, and attachments.
//    msg : message to obtain the URLs from
func ImageURLsInMessage(msg *discordgo.Message) []string {
	URLs := []string{}

	for _, a := range msg.Attachments {
		URLs = append(URLs, a.URL)
	}

	for _, embed := range msg.Embeds {
		if embed.Image != nil && embed.Image.ProxyURL != "" {
			URLs = append(URLs, embed.Image.ProxyURL)
		}
		if embed.Thumbnail != nil && embed.Thumbnail.ProxyURL != "" {
			URLs = append(URLs, embed.Thumbnail.ProxyURL)
		}
	}

	if len(msg.Embeds) == 0 {
		for _, u := range urlRegex.FindAllString(msg.Content, -1) {
			if IsImageURL(u) {
				URLs = append(URLs, u)
			}
		}
	}

	return URLs
}

// IsImageURL guesses if a URL is an image
func IsImageURL(path string) bool {
	t, err := url.Parse(path)
	if err != nil {
		log.Println("error parsing URL")
	} else {
		if HasImageSuffix(strings.ToLower(t.Path)) {
			return true
		}
	}
	return false
}

// PullImagesFromCache ...
func PullImagesFromCache(cache *MessageCache, limit int, channelID string) ([]image.Image, error) {
	images := make([]image.Image, 0, limit)

	messages, err := cache.Messages(channelID)
	if err != nil {
		return nil, err
	}

	for i := len(messages) - 1; i >= 0; i-- {
		v := messages[i]
		tmp, err := PullImagesFromMessage(limit, v)
		if err != nil {
			return nil, err
		}
		images = append(images, tmp...)

		// Decrement the image limit
		limit -= len(images)
		if limit <= 0 {
			break
		}
	}

	return images, nil
}

// HasImage returns if a message has an image
func HasImage(msg *discordgo.Message) bool {

	for _, embed := range msg.Embeds {
		if embed.Image != nil && embed.Image.URL != "" {
			return true
		}
		if embed.Thumbnail != nil && embed.Thumbnail.URL != "" {
			return true
		}
	}

	for _, v := range msg.Attachments {
		if HasImageSuffix(v.Filename) {
			return true
		}
	}

	for _, u := range urlRegex.FindAllString(msg.Content, -1) {
		if IsImageURL(u) {
			return true
		}
	}

	return false
}

// HasImageSuffix returns true if an image suffix is present
func HasImageSuffix(filename string) bool {
	for _, v := range ImageSuffixes {
		if strings.HasSuffix(filename, v) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>//go:generate stringer -type=WorldStateType -output=world_state_type_string.go

package rpg2d

import (
	"github.com/ghthor/engine/rpg2d/coord"
	"github.com/ghthor/engine/rpg2d/entity"
	"github.com/ghthor/engine/sim/stime"
)

type TerrainMapState struct {
	// Used to calculate diff's
	TerrainMap `json:"-"`

	// A Slice of new terrain the client doesn't have
	Bounds  *coord.Bounds `json:"bounds,omitempty"`
	Terrain string        `json:"terrain,omitempty"`

	// An array of type changes
	Changes []TerrainTypeChange `json:"changes,omitempty"`
}

// Prepare to be Marshalled
func (m *TerrainMapState) Prepare() {
	// Set the bounds
	m.Bounds = &m.TerrainMap.Bounds
	// Write out the Map as a string
	m.Terrain = m.TerrainMap.String()
}

func (m *TerrainMapState) IsEmpty() bool {
	if m == nil {
		return true
	}
	return m.TerrainMap.TerrainTypes == nil
}

func (m *TerrainMapState) Diff(other *TerrainMapState) (diff *TerrainMapState) {
	if m.IsEmpty() {
		return other
	}

	mBounds, oBounds := m.TerrainMap.Bounds, other.TerrainMap.Bounds
	if mBounds == oBounds {
		// No Overlaps
	} else {

		// Find the non overlapped section and set that in the diff
		switch {
		// Overlap top or bottom
		case mBounds.Width() == oBounds.Width() &&
			mBounds.TopL.X == oBounds.TopL.X &&
			mBounds.BotR.X == oBounds.BotR.X:

			if mBounds.Height() != oBounds.Height() {
				panic("invalid diff attempt")
			}

			// Overlaps the top
			if oBounds.TopL.Y > mBounds.TopL.Y {
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					oBounds.TopL,
					coord.Cell{oBounds.BotR.X, mBounds.TopL.Y + 1},
				})}

			} else if oBounds.BotR.Y < mBounds.BotR.Y {
				// Overlaps the bottom
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					coord.Cell{oBounds.TopL.X, mBounds.BotR.Y - 1},
					oBounds.BotR,
				})}
			} else {
				panic("invalid diff attempt")
			}

			// Overlaps left of right
		case mBounds.Height() == oBounds.Height() &&
			mBounds.TopL.Y == oBounds.TopL.Y &&
			mBounds.BotR.Y == oBounds.BotR.Y:

			if mBounds.Width() != oBounds.Width() {
				panic("invalid diff attempt")
			}

			// Overlaps the left
			if oBounds.TopL.X < mBounds.TopL.X {
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					oBounds.TopL,
					coord.Cell{mBounds.TopL.X - 1, oBounds.BotR.Y},
				})}
			} else if oBounds.BotR.X > mBounds.BotR.X {
				// Overlaps the right
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					coord.Cell{mBounds.BotR.X + 1, oBounds.TopL.Y},
					oBounds.BotR,
				})}
			} else {
				panic("invalid diff attempt")
			}

		default:
			panic("invalid diff attempt")
		}
	}
	return
}

func (m *TerrainMapState) Clone() (*TerrainMapState, error) {
	if m == nil {
		return m, nil
	}

	tm, err := m.TerrainMap.Clone()
	if err != nil {
		return nil, err
	}

	return &TerrainMapState{TerrainMap: tm}, nil
}

type WorldStateType int

const (
	ST_FULL WorldStateType = iota
	ST_DIFF
)

type WorldState struct {
	Type       WorldStateType   `json:"type"`
	Time       stime.Time       `json:"time"`
	Entities   []entity.State   `json:"entities"`
	Removed    []entity.State   `json:"removed"`
	TerrainMap *TerrainMapState `json:"terrainMap,omitempty"`
}

func (s WorldState) Clone() WorldState {
	terrainMap, err := s.TerrainMap.Clone()
	if err != nil {
		panic("error cloning terrain map: " + err.Error())
	}
	clone := WorldState{
		Type:       s.Type,
		Time:       s.Time,
		Entities:   make([]entity.State, len(s.Entities)),
		Removed:    nil,
		TerrainMap: terrainMap,
	}
	copy(clone.Entities, s.Entities)
	return clone
}

func (s WorldState) Cull(bounds coord.Bounds) (culled WorldState) {
	culled.Time = s.Time

	// Cull Entities
	for _, e := range s.Entities {
		if bounds.Overlaps(e.Bounds()) {
			culled.Entities = append(culled.Entities, e)
		}
	}

	// Cull Terrain
	// TODO Maybe remove the ability to have an empty TerrainMap
	// Requires updating some tests to have a terrain map that don't have one
	if !s.TerrainMap.IsEmpty() {
		culled.TerrainMap = &TerrainMapState{TerrainMap: s.TerrainMap.Slice(bounds)}
	}
	return
}

func (s WorldState) Diff(ss WorldState) (diff WorldState) {
	diff.Time = ss.Time

	if len(s.Entities) == 0 && len(ss.Entities) > 0 {
		diff.Entities = ss.Entities
	} else {
		// Find the entities that have changed from the old state to the new one
	nextEntity:
		for _, entity := range ss.Entities {
			for _, old := range s.Entities {
				if entity.Id() == old.Id() {
					if old.IsDifferentFrom(entity) {
						diff.Entities = append(diff.Entities, entity)
					}
					continue nextEntity
				}
			}
			// This is a new Entity
			diff.Entities = append(diff.Entities, entity)
		}

		// Check if all the entities in old state exist in the new state
	entityStillExists:
		for _, old := range s.Entities {
			for _, entity := range ss.Entities {
				if old.Id() == entity.Id() {
					continue entityStillExists
				}
			}
			diff.Removed = append(diff.Removed, old)
		}
	}

	// Diff the TerrainMap
	diff.TerrainMap = s.TerrainMap.Diff(ss.TerrainMap)
	return
}

// TerrainMap needs an extra step before sending
// TODO remove this maybe?
// The extra step is to avoid casting the entire terrain map to a string
// when the world state json is created. The Diff function could run this step
// and we could call it "Finalize"
func (s WorldState) Prepare() {
	if !s.TerrainMap.IsEmpty() {
		s.TerrainMap.Prepare()
	}
}
<commit_msg>[rpg2d] rename method recv and argument to improve clarity<commit_after>//go:generate stringer -type=WorldStateType -output=world_state_type_string.go

package rpg2d

import (
	"github.com/ghthor/engine/rpg2d/coord"
	"github.com/ghthor/engine/rpg2d/entity"
	"github.com/ghthor/engine/sim/stime"
)

type TerrainMapState struct {
	// Used to calculate diff's
	TerrainMap `json:"-"`

	// A Slice of new terrain the client doesn't have
	Bounds  *coord.Bounds `json:"bounds,omitempty"`
	Terrain string        `json:"terrain,omitempty"`

	// An array of type changes
	Changes []TerrainTypeChange `json:"changes,omitempty"`
}

// Prepare to be Marshalled
func (m *TerrainMapState) Prepare() {
	// Set the bounds
	m.Bounds = &m.TerrainMap.Bounds
	// Write out the Map as a string
	m.Terrain = m.TerrainMap.String()
}

func (m *TerrainMapState) IsEmpty() bool {
	if m == nil {
		return true
	}
	return m.TerrainMap.TerrainTypes == nil
}

func (m *TerrainMapState) Diff(other *TerrainMapState) (diff *TerrainMapState) {
	if m.IsEmpty() {
		return other
	}

	mBounds, oBounds := m.TerrainMap.Bounds, other.TerrainMap.Bounds
	if mBounds == oBounds {
		// No Overlaps
	} else {

		// Find the non overlapped section and set that in the diff
		switch {
		// Overlap top or bottom
		case mBounds.Width() == oBounds.Width() &&
			mBounds.TopL.X == oBounds.TopL.X &&
			mBounds.BotR.X == oBounds.BotR.X:

			if mBounds.Height() != oBounds.Height() {
				panic("invalid diff attempt")
			}

			// Overlaps the top
			if oBounds.TopL.Y > mBounds.TopL.Y {
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					oBounds.TopL,
					coord.Cell{oBounds.BotR.X, mBounds.TopL.Y + 1},
				})}

			} else if oBounds.BotR.Y < mBounds.BotR.Y {
				// Overlaps the bottom
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					coord.Cell{oBounds.TopL.X, mBounds.BotR.Y - 1},
					oBounds.BotR,
				})}
			} else {
				panic("invalid diff attempt")
			}

			// Overlaps left of right
		case mBounds.Height() == oBounds.Height() &&
			mBounds.TopL.Y == oBounds.TopL.Y &&
			mBounds.BotR.Y == oBounds.BotR.Y:

			if mBounds.Width() != oBounds.Width() {
				panic("invalid diff attempt")
			}

			// Overlaps the left
			if oBounds.TopL.X < mBounds.TopL.X {
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					oBounds.TopL,
					coord.Cell{mBounds.TopL.X - 1, oBounds.BotR.Y},
				})}
			} else if oBounds.BotR.X > mBounds.BotR.X {
				// Overlaps the right
				diff = &TerrainMapState{TerrainMap: other.Slice(coord.Bounds{
					coord.Cell{mBounds.BotR.X + 1, oBounds.TopL.Y},
					oBounds.BotR,
				})}
			} else {
				panic("invalid diff attempt")
			}

		default:
			panic("invalid diff attempt")
		}
	}
	return
}

func (m *TerrainMapState) Clone() (*TerrainMapState, error) {
	if m == nil {
		return m, nil
	}

	tm, err := m.TerrainMap.Clone()
	if err != nil {
		return nil, err
	}

	return &TerrainMapState{TerrainMap: tm}, nil
}

type WorldStateType int

const (
	ST_FULL WorldStateType = iota
	ST_DIFF
)

type WorldState struct {
	Type       WorldStateType   `json:"type"`
	Time       stime.Time       `json:"time"`
	Entities   []entity.State   `json:"entities"`
	Removed    []entity.State   `json:"removed"`
	TerrainMap *TerrainMapState `json:"terrainMap,omitempty"`
}

func (s WorldState) Clone() WorldState {
	terrainMap, err := s.TerrainMap.Clone()
	if err != nil {
		panic("error cloning terrain map: " + err.Error())
	}
	clone := WorldState{
		Type:       s.Type,
		Time:       s.Time,
		Entities:   make([]entity.State, len(s.Entities)),
		Removed:    nil,
		TerrainMap: terrainMap,
	}
	copy(clone.Entities, s.Entities)
	return clone
}

func (s WorldState) Cull(bounds coord.Bounds) (culled WorldState) {
	culled.Time = s.Time

	// Cull Entities
	for _, e := range s.Entities {
		if bounds.Overlaps(e.Bounds()) {
			culled.Entities = append(culled.Entities, e)
		}
	}

	// Cull Terrain
	// TODO Maybe remove the ability to have an empty TerrainMap
	// Requires updating some tests to have a terrain map that don't have one
	if !s.TerrainMap.IsEmpty() {
		culled.TerrainMap = &TerrainMapState{TerrainMap: s.TerrainMap.Slice(bounds)}
	}
	return
}

func (state WorldState) Diff(other WorldState) (diff WorldState) {
	diff.Time = other.Time

	if len(state.Entities) == 0 && len(other.Entities) > 0 {
		diff.Entities = other.Entities
	} else {
		// Find the entities that have changed from the old state to the new one
	nextEntity:
		for _, entity := range other.Entities {
			for _, old := range state.Entities {
				if entity.Id() == old.Id() {
					if old.IsDifferentFrom(entity) {
						diff.Entities = append(diff.Entities, entity)
					}
					continue nextEntity
				}
			}
			// This is a new Entity
			diff.Entities = append(diff.Entities, entity)
		}

		// Check if all the entities in old state exist in the new state
	entityStillExists:
		for _, old := range state.Entities {
			for _, entity := range other.Entities {
				if old.Id() == entity.Id() {
					continue entityStillExists
				}
			}
			diff.Removed = append(diff.Removed, old)
		}
	}

	// Diff the TerrainMap
	diff.TerrainMap = state.TerrainMap.Diff(other.TerrainMap)
	return
}

// TerrainMap needs an extra step before sending
// TODO remove this maybe?
// The extra step is to avoid casting the entire terrain map to a string
// when the world state json is created. The Diff function could run this step
// and we could call it "Finalize"
func (s WorldState) Prepare() {
	if !s.TerrainMap.IsEmpty() {
		s.TerrainMap.Prepare()
	}
}
<|endoftext|>


<commit_before>package environmentvariablegroup

import (
	"github.com/cloudfoundry/cli/cf/api/environment_variable_groups"
	"github.com/cloudfoundry/cli/cf/command_metadata"
	"github.com/cloudfoundry/cli/cf/configuration"
	cf_errors "github.com/cloudfoundry/cli/cf/errors"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/codegangsta/cli"
)

type SetRunningEnvironmentVariableGroup struct {
	ui                           terminal.UI
	config                       configuration.ReadWriter
	environmentVariableGroupRepo environment_variable_groups.EnvironmentVariableGroupsRepository
}

func NewSetRunningEnvironmentVariableGroup(ui terminal.UI, config configuration.ReadWriter, environmentVariableGroupRepo environment_variable_groups.EnvironmentVariableGroupsRepository) (cmd SetRunningEnvironmentVariableGroup) {
	cmd.ui = ui
	cmd.config = config
	cmd.environmentVariableGroupRepo = environmentVariableGroupRepo
	return
}

func (cmd SetRunningEnvironmentVariableGroup) Metadata() command_metadata.CommandMetadata {
	return command_metadata.CommandMetadata{
		Name:        "set-running-environment-variable-group",
		Description: T("Pass parameters as JSON to create a running environment variable group"),
		ShortName:   "srevg",
		Usage:       T("CF_NAME set-running-environment-variable-group"),
	}
}

func (cmd SetRunningEnvironmentVariableGroup) GetRequirements(requirementsFactory requirements.Factory, c *cli.Context) ([]requirements.Requirement, error) {
	if len(c.Args()) != 1 {
		cmd.ui.FailWithUsage(c)
	}

	reqs := []requirements.Requirement{
		requirementsFactory.NewLoginRequirement(),
	}
	return reqs, nil
}

func (cmd SetRunningEnvironmentVariableGroup) Run(c *cli.Context) {
	cmd.ui.Say(T("Setting the contents of the running environment variable group as {{.Username}}...", map[string]interface{}{
		"Username": terminal.EntityNameColor(cmd.config.Username())}))

	err := cmd.environmentVariableGroupRepo.SetRunning(c.Args()[0])
	if err != nil {
		suggestionText := ""

		httpError, ok := err.(cf_errors.HttpError)
		if ok && httpError.ErrorCode() == cf_errors.PARSE_ERROR {
			suggestionText = T(`

Your JSON string syntax is invalid.  Proper syntax is this:  cf set-running-environment-variable-group '{"name":"value","name":"value"}'`)
		}
		cmd.ui.Failed(err.Error() + suggestionText)
	}

	cmd.ui.Ok()
}
<commit_msg>Modify srevg command usage to show example.<commit_after>package environmentvariablegroup

import (
	"github.com/cloudfoundry/cli/cf/api/environment_variable_groups"
	"github.com/cloudfoundry/cli/cf/command_metadata"
	"github.com/cloudfoundry/cli/cf/configuration"
	cf_errors "github.com/cloudfoundry/cli/cf/errors"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/codegangsta/cli"
)

type SetRunningEnvironmentVariableGroup struct {
	ui                           terminal.UI
	config                       configuration.ReadWriter
	environmentVariableGroupRepo environment_variable_groups.EnvironmentVariableGroupsRepository
}

func NewSetRunningEnvironmentVariableGroup(ui terminal.UI, config configuration.ReadWriter, environmentVariableGroupRepo environment_variable_groups.EnvironmentVariableGroupsRepository) (cmd SetRunningEnvironmentVariableGroup) {
	cmd.ui = ui
	cmd.config = config
	cmd.environmentVariableGroupRepo = environmentVariableGroupRepo
	return
}

func (cmd SetRunningEnvironmentVariableGroup) Metadata() command_metadata.CommandMetadata {
	return command_metadata.CommandMetadata{
		Name:        "set-running-environment-variable-group",
		Description: T("Pass parameters as JSON to create a running environment variable group"),
		ShortName:   "srevg",
		Usage:       T(`CF_NAME set-running-environment-variable-group '{"name":"value","name":"value"}'`),
	}
}

func (cmd SetRunningEnvironmentVariableGroup) GetRequirements(requirementsFactory requirements.Factory, c *cli.Context) ([]requirements.Requirement, error) {
	if len(c.Args()) != 1 {
		cmd.ui.FailWithUsage(c)
	}

	reqs := []requirements.Requirement{
		requirementsFactory.NewLoginRequirement(),
	}
	return reqs, nil
}

func (cmd SetRunningEnvironmentVariableGroup) Run(c *cli.Context) {
	cmd.ui.Say(T("Setting the contents of the running environment variable group as {{.Username}}...", map[string]interface{}{
		"Username": terminal.EntityNameColor(cmd.config.Username())}))

	err := cmd.environmentVariableGroupRepo.SetRunning(c.Args()[0])
	if err != nil {
		suggestionText := ""

		httpError, ok := err.(cf_errors.HttpError)
		if ok && httpError.ErrorCode() == cf_errors.PARSE_ERROR {
			suggestionText = T(`

Your JSON string syntax is invalid.  Proper syntax is this:  cf set-running-environment-variable-group '{"name":"value","name":"value"}'`)
		}
		cmd.ui.Failed(err.Error() + suggestionText)
	}

	cmd.ui.Ok()
}
<|endoftext|>


<commit_before>package builder

import (
	"github.com/antham/strumt"

	"fmt"
)

// NewSwitchPrompt creates a new prompt used to provides several choices, like a menu can do
func NewSwitchPrompt(ID string, choices []SwitchConfig) strumt.Prompter {
	return &switchPrompt{ID, choices}
}

// SwitchConfig provides a configuration to switch prompt
type SwitchConfig struct {
	Choice       string
	PromptString string
	NextPromptID string
}

type switchPrompt struct {
	iD      string
	choices []SwitchConfig
}

func (s *switchPrompt) ID() string {
	return s.iD
}

func (s *switchPrompt) PromptString() string {
	out := fmt.Sprintf("Choose one of this option and press enter:\n")

	for _, choice := range s.choices {
		out += fmt.Sprintf("%s - %s\n", choice.Choice, choice.PromptString)
	}

	return out
}

func (s *switchPrompt) Parse(value string) error {
	if value == "" {
		return fmt.Errorf("No value given")
	}

	for _, choice := range s.choices {
		if choice.Choice == value {
			return nil
		}
	}

	return fmt.Errorf("This choice doesn't exist")
}

func (s *switchPrompt) NextOnSuccess(value string) string {
	for _, choice := range s.choices {
		if choice.Choice == value {
			return choice.NextPromptID
		}
	}

	return ""
}

func (s *switchPrompt) NextOnError(err error) string {
	return s.iD
}

func (s *switchPrompt) PrintPrompt(prompt string) {
	fmt.Printf("%s", prompt)
}
<commit_msg>ref(prompt) : remove useless function<commit_after>package builder

import (
	"github.com/antham/strumt"

	"fmt"
)

// NewSwitchPrompt creates a new prompt used to provides several choices, like a menu can do
func NewSwitchPrompt(ID string, choices []SwitchConfig) strumt.Prompter {
	return &switchPrompt{ID, choices}
}

// SwitchConfig provides a configuration to switch prompt
type SwitchConfig struct {
	Choice       string
	PromptString string
	NextPromptID string
}

type switchPrompt struct {
	iD      string
	choices []SwitchConfig
}

func (s *switchPrompt) ID() string {
	return s.iD
}

func (s *switchPrompt) PromptString() string {
	out := fmt.Sprintf("Choose one of this option and press enter:\n")

	for _, choice := range s.choices {
		out += fmt.Sprintf("%s - %s\n", choice.Choice, choice.PromptString)
	}

	return out
}

func (s *switchPrompt) Parse(value string) error {
	if value == "" {
		return fmt.Errorf("No value given")
	}

	for _, choice := range s.choices {
		if choice.Choice == value {
			return nil
		}
	}

	return fmt.Errorf("This choice doesn't exist")
}

func (s *switchPrompt) NextOnSuccess(value string) string {
	for _, choice := range s.choices {
		if choice.Choice == value {
			return choice.NextPromptID
		}
	}

	return ""
}

func (s *switchPrompt) NextOnError(err error) string {
	return s.iD
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fs

import (
	"fmt"
	"reflect"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"github.com/jacobsa/gcsfuse/fs/inode"
	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"
)

// Create a fuse file system whose root directory is the root of the supplied
// bucket. The supplied clock will be used for cache invalidation, modification
// times, etc.
func NewFuseFS(
	clock timeutil.Clock,
	bucket gcs.Bucket) (ffs fuse.FileSystem, err error) {
	fs := &fileSystem{
		clock:  clock,
		bucket: bucket,
	}

	fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)

	ffs = fs
	return
}

type fileSystem struct {
	fuseutil.NotImplementedFileSystem

	/////////////////////////
	// Dependencies
	/////////////////////////

	clock  timeutil.Clock
	bucket gcs.Bucket

	/////////////////////////
	// Mutable state
	/////////////////////////

	// When acquiring this lock, the caller must hold no inode locks.
	mu syncutil.InvariantMutex

	// The collection of live inodes, indexed by ID. IDs of free inodes that may
	// be re-used have nil entries. No ID less than fuse.RootInodeID is ever used.
	//
	// INVARIANT: All elements are nil or of type *inode.(Dir|File)Inode
	// INVARIANT: len(inodes) > fuse.RootInodeID
	// INVARIANT: For all i < fuse.RootInodeID, inodes[i] == nil
	// INVARIANT: inodes[fuse.RootInodeID] != nil
	// INVARIANT: inodes[fuse.RootInodeID] is of type *inode.DirInode
	//
	// GUARDED_BY(mu)
	inodes []interface{}

	// A list of inode IDs within inodes available for reuse, not including the
	// reserved IDs less than fuse.RootInodeID.
	//
	// INVARIANT: This is all and only indices i of 'inodes' such that i >
	// fuse.RootInodeID and inodes[i] == nil
	//
	// GUARDED_BY(mu)
	freeInodeIDs []fuse.InodeID
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (fs *fileSystem) checkInvariants() {
	// Check reserved inodes.
	for i := 0; i < fuse.RootInodeID; i++ {
		if fs.inodes[i] != nil {
			panic(fmt.Sprintf("Non-nil inode for ID: %v", i))
		}
	}

	// Check the root inode.
	_ = fs.inodes[fuse.RootInodeID].(*inode.DirInode)

	// Check the type of each inode. While we're at it, build our own list of
	// free IDs.
	freeIDsEncountered := make(map[fuse.InodeID]struct{})
	for i := fuse.RootInodeID + 1; i < len(fs.inodes); i++ {
		in := fs.inodes[i]
		switch in.(type) {
		case *inode.DirInode:
		case *inode.FileInode:

		case nil:
			freeIDsEncountered[fuse.InodeID(i)] = struct{}{}

		default:
			panic(fmt.Sprintf("Unexpected inode type: %v", reflect.TypeOf(in)))
		}
	}

	// Check fs.freeInodeIDs.
	if len(fs.freeInodeIDs) != len(freeIDsEncountered) {
		panic(
			fmt.Sprintf(
				"Length mismatch: %v vs. %v",
				len(fs.freeInodeIDs),
				len(freeIDsEncountered)))
	}

	for _, id := range fs.freeInodeIDs {
		if _, ok := freeIDsEncountered[id]; !ok {
			panic(fmt.Sprintf("Unexected free inode ID: %v", id))
		}
	}
}

////////////////////////////////////////////////////////////////////////
// fuse.FileSystem methods
////////////////////////////////////////////////////////////////////////

func (fs *fileSystem) Init(
	ctx context.Context,
	req *fuse.InitRequest) (resp *fuse.InitResponse, err error) {
	// Nothing interesting to do.
	resp = &fuse.InitResponse{}
	return
}
<commit_msg>Implemented fileSystem.OpenDir.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fs

import (
	"fmt"
	"reflect"

	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fuseutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"github.com/jacobsa/gcsfuse/fs/inode"
	"github.com/jacobsa/gcsfuse/timeutil"
	"golang.org/x/net/context"
)

// Create a fuse file system whose root directory is the root of the supplied
// bucket. The supplied clock will be used for cache invalidation, modification
// times, etc.
func NewFuseFS(
	clock timeutil.Clock,
	bucket gcs.Bucket) (ffs fuse.FileSystem, err error) {
	fs := &fileSystem{
		clock:  clock,
		bucket: bucket,
	}

	fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)

	ffs = fs
	return
}

type fileSystem struct {
	fuseutil.NotImplementedFileSystem

	/////////////////////////
	// Dependencies
	/////////////////////////

	clock  timeutil.Clock
	bucket gcs.Bucket

	/////////////////////////
	// Mutable state
	/////////////////////////

	// When acquiring this lock, the caller must hold no inode locks.
	mu syncutil.InvariantMutex

	// The collection of live inodes, indexed by ID. IDs of free inodes that may
	// be re-used have nil entries. No ID less than fuse.RootInodeID is ever used.
	//
	// INVARIANT: All elements are nil or of type *inode.(Dir|File)Inode
	// INVARIANT: len(inodes) > fuse.RootInodeID
	// INVARIANT: For all i < fuse.RootInodeID, inodes[i] == nil
	// INVARIANT: inodes[fuse.RootInodeID] != nil
	// INVARIANT: inodes[fuse.RootInodeID] is of type *inode.DirInode
	//
	// GUARDED_BY(mu)
	inodes []interface{}

	// A list of inode IDs within inodes available for reuse, not including the
	// reserved IDs less than fuse.RootInodeID.
	//
	// INVARIANT: This is all and only indices i of 'inodes' such that i >
	// fuse.RootInodeID and inodes[i] == nil
	//
	// GUARDED_BY(mu)
	freeInodeIDs []fuse.InodeID
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func (fs *fileSystem) checkInvariants() {
	// Check reserved inodes.
	for i := 0; i < fuse.RootInodeID; i++ {
		if fs.inodes[i] != nil {
			panic(fmt.Sprintf("Non-nil inode for ID: %v", i))
		}
	}

	// Check the root inode.
	_ = fs.inodes[fuse.RootInodeID].(*inode.DirInode)

	// Check the type of each inode. While we're at it, build our own list of
	// free IDs.
	freeIDsEncountered := make(map[fuse.InodeID]struct{})
	for i := fuse.RootInodeID + 1; i < len(fs.inodes); i++ {
		in := fs.inodes[i]
		switch in.(type) {
		case *inode.DirInode:
		case *inode.FileInode:

		case nil:
			freeIDsEncountered[fuse.InodeID(i)] = struct{}{}

		default:
			panic(fmt.Sprintf("Unexpected inode type: %v", reflect.TypeOf(in)))
		}
	}

	// Check fs.freeInodeIDs.
	if len(fs.freeInodeIDs) != len(freeIDsEncountered) {
		panic(
			fmt.Sprintf(
				"Length mismatch: %v vs. %v",
				len(fs.freeInodeIDs),
				len(freeIDsEncountered)))
	}

	for _, id := range fs.freeInodeIDs {
		if _, ok := freeIDsEncountered[id]; !ok {
			panic(fmt.Sprintf("Unexected free inode ID: %v", id))
		}
	}
}

// Find the given inode and return it with its lock held for reading. Panic if
// it doesn't exist or is the wrong type.
//
// SHARED_LOCKS_REQUIRED(fs.mu)
// SHARED_LOCK_FUNCTION(inode.mu)
func (fs *fileSystem) getDirForReadingOrDie(
	id fuse.InodeID) (in *inode.DirInode) {
	in = fs.inodes[id].(*inode.DirInode)
	in.Mu.RLock()
	return
}

////////////////////////////////////////////////////////////////////////
// fuse.FileSystem methods
////////////////////////////////////////////////////////////////////////

func (fs *fileSystem) Init(
	ctx context.Context,
	req *fuse.InitRequest) (resp *fuse.InitResponse, err error) {
	// Nothing interesting to do.
	resp = &fuse.InitResponse{}
	return
}

func (fs *fileSystem) OpenDir(
	ctx context.Context,
	req *fuse.OpenDirRequest) (resp *fuse.OpenDirResponse, err error) {
	resp = &fuse.OpenDirResponse{}

	fs.mu.RLock()
	defer fs.mu.RUnlock()

	// Make sure the inode still exists and is a directory. If not, something has
	// screwed up because the VFS layer shouldn't have let us forget the inode
	// before opening it.
	in := fs.getDirForReadingOrDie(req.Inode)
	defer in.Mu.RUnlock()

	return
}
<|endoftext|>


<commit_before>// Copyright 2015 go-swagger maintainers
//
// 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 spec

import (
	"encoding/json"
	"net/http"
	"os"
	"path/filepath"

	"github.com/go-openapi/jsonreference"
)

// Refable is a struct for things that accept a $ref property
type Refable struct {
	Ref Ref
}

// MarshalJSON marshals the ref to json
func (r Refable) MarshalJSON() ([]byte, error) {
	return r.Ref.MarshalJSON()
}

// UnmarshalJSON unmarshalss the ref from json
func (r *Refable) UnmarshalJSON(d []byte) error {
	return json.Unmarshal(d, &r.Ref)
}

// Ref represents a json reference that is potentially resolved
type Ref struct {
	jsonreference.Ref
}

// RemoteURI gets the remote uri part of the ref
func (r *Ref) RemoteURI() string {
	if r.String() == "" {
		return r.String()
	}

	u := *r.GetURL()
	u.Fragment = ""
	return u.String()
}

// IsValidURI returns true when the url the ref points to can be found
func (r *Ref) IsValidURI(basepaths ...string) bool {
	if r.String() == "" {
		return true
	}

	v := r.RemoteURI()
	if v == "" {
		return true
	}

	if r.HasFullURL {
		rr, err := http.Get(v)
		if err != nil {
			return false
		}

		return rr.StatusCode/100 == 2
	}

	if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) {
		return false
	}

	// check for local file
	pth := v
	if r.HasURLPathOnly {
		base := "."
		if len(basepaths) > 0 {
			base = filepath.Dir(filepath.Join(basepaths...))
		}
		p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth)))
		if e != nil {
			return false
		}
		pth = p
	}

	fi, err := os.Stat(filepath.ToSlash(pth))
	if err != nil {
		return false
	}

	return !fi.IsDir()
}

// Inherits creates a new reference from a parent and a child
// If the child cannot inherit from the parent, an error is returned
func (r *Ref) Inherits(child Ref) (*Ref, error) {
	ref, err := r.Ref.Inherits(child.Ref)
	if err != nil {
		return nil, err
	}
	return &Ref{Ref: *ref}, nil
}

// NewRef creates a new instance of a ref object
// returns an error when the reference uri is an invalid uri
func NewRef(refURI string) (Ref, error) {
	ref, err := jsonreference.New(refURI)
	if err != nil {
		return Ref{}, err
	}
	return Ref{Ref: ref}, nil
}

// MustCreateRef creates a ref object but
func MustCreateRef(refURI string) Ref {
	return Ref{Ref: jsonreference.MustCreateRef(refURI)}
}

// // NewResolvedRef creates a resolved ref
// func NewResolvedRef(refURI string, data interface{}) Ref {
// 	return Ref{
// 		Ref:      jsonreference.MustCreateRef(refURI),
// 		Resolved: data,
// 	}
// }

// MarshalJSON marshals this ref into a JSON object
func (r Ref) MarshalJSON() ([]byte, error) {
	str := r.String()
	if str == "" {
		if r.IsRoot() {
			return []byte(`{"$ref":"#"}`), nil
		}
		return []byte("{}"), nil
	}
	v := map[string]interface{}{"$ref": str}
	return json.Marshal(v)
}

// UnmarshalJSON unmarshals this ref from a JSON object
func (r *Ref) UnmarshalJSON(d []byte) error {
	var v map[string]interface{}
	if err := json.Unmarshal(d, &v); err != nil {
		return err
	}

	if v == nil {
		return nil
	}

	if vv, ok := v["$ref"]; ok {
		if str, ok := vv.(string); ok {
			ref, err := jsonreference.New(str)
			if err != nil {
				return err
			}
			*r = Ref{Ref: ref}
		}
	}

	return nil
}
<commit_msg>return empty ref instead of empty fragment for root ref<commit_after>// Copyright 2015 go-swagger maintainers
//
// 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 spec

import (
	"encoding/json"
	"net/http"
	"os"
	"path/filepath"

	"github.com/go-openapi/jsonreference"
)

// Refable is a struct for things that accept a $ref property
type Refable struct {
	Ref Ref
}

// MarshalJSON marshals the ref to json
func (r Refable) MarshalJSON() ([]byte, error) {
	return r.Ref.MarshalJSON()
}

// UnmarshalJSON unmarshalss the ref from json
func (r *Refable) UnmarshalJSON(d []byte) error {
	return json.Unmarshal(d, &r.Ref)
}

// Ref represents a json reference that is potentially resolved
type Ref struct {
	jsonreference.Ref
}

// RemoteURI gets the remote uri part of the ref
func (r *Ref) RemoteURI() string {
	if r.String() == "" {
		return r.String()
	}

	u := *r.GetURL()
	u.Fragment = ""
	return u.String()
}

// IsValidURI returns true when the url the ref points to can be found
func (r *Ref) IsValidURI(basepaths ...string) bool {
	if r.String() == "" {
		return true
	}

	v := r.RemoteURI()
	if v == "" {
		return true
	}

	if r.HasFullURL {
		rr, err := http.Get(v)
		if err != nil {
			return false
		}

		return rr.StatusCode/100 == 2
	}

	if !(r.HasFileScheme || r.HasFullFilePath || r.HasURLPathOnly) {
		return false
	}

	// check for local file
	pth := v
	if r.HasURLPathOnly {
		base := "."
		if len(basepaths) > 0 {
			base = filepath.Dir(filepath.Join(basepaths...))
		}
		p, e := filepath.Abs(filepath.ToSlash(filepath.Join(base, pth)))
		if e != nil {
			return false
		}
		pth = p
	}

	fi, err := os.Stat(filepath.ToSlash(pth))
	if err != nil {
		return false
	}

	return !fi.IsDir()
}

// Inherits creates a new reference from a parent and a child
// If the child cannot inherit from the parent, an error is returned
func (r *Ref) Inherits(child Ref) (*Ref, error) {
	ref, err := r.Ref.Inherits(child.Ref)
	if err != nil {
		return nil, err
	}
	return &Ref{Ref: *ref}, nil
}

// NewRef creates a new instance of a ref object
// returns an error when the reference uri is an invalid uri
func NewRef(refURI string) (Ref, error) {
	ref, err := jsonreference.New(refURI)
	if err != nil {
		return Ref{}, err
	}
	return Ref{Ref: ref}, nil
}

// MustCreateRef creates a ref object but
func MustCreateRef(refURI string) Ref {
	return Ref{Ref: jsonreference.MustCreateRef(refURI)}
}

// MarshalJSON marshals this ref into a JSON object
func (r Ref) MarshalJSON() ([]byte, error) {
	str := r.String()
	if str == "" {
		if r.IsRoot() {
			return []byte(`{"$ref":""}`), nil
		}
		return []byte("{}"), nil
	}
	v := map[string]interface{}{"$ref": str}
	return json.Marshal(v)
}

// UnmarshalJSON unmarshals this ref from a JSON object
func (r *Ref) UnmarshalJSON(d []byte) error {
	var v map[string]interface{}
	if err := json.Unmarshal(d, &v); err != nil {
		return err
	}

	if v == nil {
		return nil
	}

	if vv, ok := v["$ref"]; ok {
		if str, ok := vv.(string); ok {
			ref, err := jsonreference.New(str)
			if err != nil {
				return err
			}
			*r = Ref{Ref: ref}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/libgit2/git2go"
)

// type Repos struct {
// 	repo []Repo
// }

type Repo struct {
	URL        string
	Path       string
	repository *git.Repository
}

var clone = "./test"
var repo = "https://gitlab.com/rollbrettler/go-playground.git"

func main() {

	// repos := []Repos{
	// 	{Url: "https://gitlab.com/rollbrettler/go-playground.git", Path: "./test"},
	// 	{Url: "https://github.com/rollbrettler/compress-videos.git", Path: "./test2"},
	// }
	//
	// for _, repo := range repos {
	// 	fmt.Printf("%s -> %s\n", repo.URL, repo.Path)
	// }

	var err error

	repo := Repo{URL: "https://gitlab.com/rollbrettler/go-playground.git", Path: "./test.git"}

	err = repo.openRepository()
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	err = repo.updateRepository()
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	log.Println("Sucessfully updated")
	os.Exit(0)
}

func (repo Repo) cloneRepository() (err error) {

	cloneOptions := &git.CloneOptions{
		Bare: true,
	}

	repo.repository, err = git.Clone(repo.URL, repo.Path, cloneOptions)
	if err != nil {
		log.Println(err)
		return
	}

	config, err := repo.repository.Config()
	if err != nil {
		log.Println(err)
		return
	}

	err = repo.changeToMirrorConfig(*config)
	if err != nil {
		log.Println(err)
	}

	err = repo.updateRepository()
	if err != nil {
		log.Println(err)
	}

	return nil
}

func (repo Repo) changeToMirrorConfig(config git.Config) (err error) {

	fetch, err := config.LookupString("remote.origin.fetch")
	if err != nil {
		return err
	}

	mirror, err := config.LookupBool("remote.origin.mirror")
	if err != nil {
		return err
	}

	log.Println(fmt.Sprintf("Refspec: %v\n", fetch))
	config.SetString("remote.origin.fetch", "+refs/*:refs/*")

	log.Println(fmt.Sprintf("Mirror: %v\n", mirror))
	config.SetBool("remote.origin.mirror", true)

	return nil
}

func (repo Repo) updateRepository() (err error) {
	remotes := repo.repository.Remotes
	remote, err := remotes.Lookup("origin")
	defer remote.Free()

	err = remote.Fetch([]string{}, nil, "")
	if err != nil {
		return err
	}

	return nil
}

func (repo Repo) openRepository() (err error) {
	_, err = ioutil.ReadDir(repo.Path)
	if err != nil {
		log.Println("Directory not present")

		err := repo.cloneRepository()
		if err != nil {
			log.Println(err)
			os.Exit(1)
		}
	}

	log.Println("Folder already cloned")

	repo.repository, err = git.OpenRepository(repo.Path)
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	return err
}
<commit_msg>Revert wrong extraction<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/libgit2/git2go"
)

// type Repos struct {
// 	repo []Repo
// }

type Repo struct {
	URL        string
	Path       string
	repository *git.Repository
}

var clone = "./test"
var repo = "https://gitlab.com/rollbrettler/go-playground.git"

func main() {

	// repos := []Repos{
	// 	{Url: "https://gitlab.com/rollbrettler/go-playground.git", Path: "./test"},
	// 	{Url: "https://github.com/rollbrettler/compress-videos.git", Path: "./test2"},
	// }
	//
	// for _, repo := range repos {
	// 	fmt.Printf("%s -> %s\n", repo.URL, repo.Path)
	// }

	var err error

	repo := Repo{URL: "https://gitlab.com/rollbrettler/go-playground.git", Path: "./test.git"}

	err = repo.openRepository()
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	err = repo.updateRepository()
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	log.Println("Sucessfully updated")
	os.Exit(0)
}

func (repo Repo) cloneRepository() (err error) {

	cloneOptions := &git.CloneOptions{
		Bare: true,
	}

	repo.repository, err = git.Clone(repo.URL, repo.Path, cloneOptions)
	if err != nil {
		log.Println(err)
		return
	}

	config, err := repo.repository.Config()
	if err != nil {
		log.Println(err)
		return
	}

	err = repo.changeToMirrorConfig(*config)
	if err != nil {
		log.Println(err)
	}

	err = repo.updateRepository()
	if err != nil {
		log.Println(err)
	}

	return nil
}

func (repo Repo) changeToMirrorConfig(config git.Config) (err error) {

	fetch, err := config.LookupString("remote.origin.fetch")
	if err != nil {
		return err
	}

	mirror, err := config.LookupBool("remote.origin.mirror")
	if err != nil {
		return err
	}

	log.Println(fmt.Sprintf("Refspec: %v\n", fetch))
	config.SetString("remote.origin.fetch", "+refs/*:refs/*")

	log.Println(fmt.Sprintf("Mirror: %v\n", mirror))
	config.SetBool("remote.origin.mirror", true)

	return nil
}

func (repo Repo) updateRepository() (err error) {
	remote, err := repo.repository.Remotes.Lookup("origin")
	defer remote.Free()

	err = remote.Fetch([]string{}, nil, "")
	if err != nil {
		return err
	}

	return nil
}

func (repo Repo) openRepository() (err error) {
	_, err = ioutil.ReadDir(repo.Path)
	if err != nil {
		log.Println("Directory not present")

		err := repo.cloneRepository()
		if err != nil {
			log.Println(err)
			os.Exit(1)
		}
	}

	log.Println("Folder already cloned")

	repo.repository, err = git.OpenRepository(repo.Path)
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	return err
}
<|endoftext|>


<commit_before>package commands

import (
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"os"
	gopath "path"
	"strings"

	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"

	cmds "github.com/ipfs/go-ipfs/commands"
	core "github.com/ipfs/go-ipfs/core"
	path "github.com/ipfs/go-ipfs/path"
	tar "github.com/ipfs/go-ipfs/thirdparty/tar"
	utar "github.com/ipfs/go-ipfs/unixfs/tar"
)

var ErrInvalidCompressionLevel = errors.New("Compression level must be between 1 and 9")

var GetCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Download IPFS objects",
		ShortDescription: `
Retrieves the object named by <ipfs-or-ipns-path> and stores the data to disk.

By default, the output will be stored at ./<ipfs-path>, but an alternate path
can be specified with '--output=<path>' or '-o=<path>'.

To output a TAR archive instead of unpacked files, use '--archive' or '-a'.

To compress the output with GZIP compression, use '--compress' or '-C'. You
may also specify the level of compression by specifying '-l=<1-9>'.
`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("ipfs-path", true, false, "The path to the IPFS object(s) to be outputted").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.StringOption("output", "o", "The path where output should be stored"),
		cmds.BoolOption("archive", "a", "Output a TAR archive"),
		cmds.BoolOption("compress", "C", "Compress the output with GZIP compression"),
		cmds.IntOption("compression-level", "l", "The level of compression (1-9)"),
	},
	PreRun: func(req cmds.Request) error {
		_, err := getCompressOptions(req)
		return err
	},
	Run: func(req cmds.Request, res cmds.Response) {
		cmplvl, err := getCompressOptions(req)
		if err != nil {
			res.SetError(err, cmds.ErrClient)
			return
		}

		node, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		reader, err := get(req.Context().Context, node, req.Arguments()[0], cmplvl)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		res.SetOutput(reader)
	},
	PostRun: func(req cmds.Request, res cmds.Response) {
		if res.Output() == nil {
			return
		}
		outReader := res.Output().(io.Reader)
		res.SetOutput(nil)

		outPath, _, _ := req.Option("output").String()
		if len(outPath) == 0 {
			_, outPath = gopath.Split(req.Arguments()[0])
			outPath = gopath.Clean(outPath)
		}

		cmplvl, err := getCompressOptions(req)
		if err != nil {
			res.SetError(err, cmds.ErrClient)
			return
		}

		if archive, _, _ := req.Option("archive").Bool(); archive {
			if !strings.HasSuffix(outPath, ".tar") {
				outPath += ".tar"
			}
			if cmplvl != gzip.NoCompression {
				outPath += ".gz"
			}
			fmt.Printf("Saving archive to %s\n", outPath)

			file, err := os.Create(outPath)
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
			defer file.Close()

			bar := pb.New(0).SetUnits(pb.U_BYTES)
			bar.Output = os.Stderr
			pbReader := bar.NewProxyReader(outReader)
			bar.Start()
			defer bar.Finish()

			_, err = io.Copy(file, pbReader)
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}

			return
		}

		fmt.Printf("Saving file(s) to %s\n", outPath)

		// TODO: get total length of files
		bar := pb.New(0).SetUnits(pb.U_BYTES)
		bar.Output = os.Stderr

		// wrap the reader with the progress bar proxy reader
		// if the output is compressed, also wrap it in a gzip.Reader
		var reader io.Reader
		if cmplvl != gzip.NoCompression {
			gzipReader, err := gzip.NewReader(outReader)
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
			defer gzipReader.Close()
			reader = bar.NewProxyReader(gzipReader)
		} else {
			reader = bar.NewProxyReader(outReader)
		}

		bar.Start()
		defer bar.Finish()

		extractor := &tar.Extractor{outPath}
		err = extractor.Extract(reader)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
		}
	},
}

func getCompressOptions(req cmds.Request) (int, error) {
	cmprs, _, _ := req.Option("compress").Bool()
	cmplvl, cmplvlFound, _ := req.Option("compression-level").Int()
	switch {
	case !cmprs:
		return gzip.NoCompression, nil
	case cmprs && !cmplvlFound:
		return gzip.DefaultCompression, nil
	case cmprs && cmplvlFound && (cmplvl < 1 || cmplvl > 9):
		return gzip.NoCompression, ErrInvalidCompressionLevel
	}
	return gzip.NoCompression, nil
}

func get(ctx context.Context, node *core.IpfsNode, p string, compression int) (io.Reader, error) {
	pathToResolve := path.Path(p)
	dagnode, err := core.Resolve(ctx, node, pathToResolve)
	if err != nil {
		return nil, err
	}

	return utar.NewReader(pathToResolve, node.DAG, dagnode, compression)
}
<commit_msg>Don't use tar reader for '-C' only flag<commit_after>package commands

import (
	"bufio"
	"compress/gzip"
	"errors"
	"fmt"
	"io"
	"os"
	gopath "path"
	"strings"

	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/cheggaaa/pb"
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"

	cmds "github.com/ipfs/go-ipfs/commands"
	core "github.com/ipfs/go-ipfs/core"
	path "github.com/ipfs/go-ipfs/path"
	tar "github.com/ipfs/go-ipfs/thirdparty/tar"
	uio "github.com/ipfs/go-ipfs/unixfs/io"
	utar "github.com/ipfs/go-ipfs/unixfs/tar"
)

var ErrInvalidCompressionLevel = errors.New("Compression level must be between 1 and 9")

var GetCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Download IPFS objects",
		ShortDescription: `
Retrieves the object named by <ipfs-or-ipns-path> and stores the data to disk.

By default, the output will be stored at ./<ipfs-path>, but an alternate path
can be specified with '--output=<path>' or '-o=<path>'.

To output a TAR archive instead of unpacked files, use '--archive' or '-a'.

To compress the output with GZIP compression, use '--compress' or '-C'. You
may also specify the level of compression by specifying '-l=<1-9>'.
`,
	},

	Arguments: []cmds.Argument{
		cmds.StringArg("ipfs-path", true, false, "The path to the IPFS object(s) to be outputted").EnableStdin(),
	},
	Options: []cmds.Option{
		cmds.StringOption("output", "o", "The path where output should be stored"),
		cmds.BoolOption("archive", "a", "Output a TAR archive"),
		cmds.BoolOption("compress", "C", "Compress the output with GZIP compression"),
		cmds.IntOption("compression-level", "l", "The level of compression (1-9)"),
	},
	PreRun: func(req cmds.Request) error {
		_, err := getCompressOptions(req)
		return err
	},
	Run: func(req cmds.Request, res cmds.Response) {
		cmplvl, err := getCompressOptions(req)
		if err != nil {
			res.SetError(err, cmds.ErrClient)
			return
		}

		node, err := req.Context().GetNode()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		// Validate path string
		p, err := path.ParsePath(req.Arguments()[0])
		if err != nil {
			res.SetError(fmt.Errorf("failed to validate path: %v", err), cmds.ErrNormal)
			return
		}
		var reader io.Reader

		if archive, _, _ := req.Option("archive").Bool(); !archive && cmplvl != gzip.NoCompression {
			// only use this when the flag is '-C' without '-a'
			reader, err = getZip(req.Context().Context, node, p, cmplvl)
		} else {
			reader, err = get(req.Context().Context, node, p, cmplvl)
		}
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}
		res.SetOutput(reader)
	},
	PostRun: func(req cmds.Request, res cmds.Response) {
		if res.Output() == nil {
			return
		}
		outReader := res.Output().(io.Reader)
		res.SetOutput(nil)

		outPath, _, _ := req.Option("output").String()
		if len(outPath) == 0 {
			_, outPath = gopath.Split(req.Arguments()[0])
			outPath = gopath.Clean(outPath)
		}

		cmplvl, err := getCompressOptions(req)
		if err != nil {
			res.SetError(err, cmds.ErrClient)
			return
		}

		if archive, _, _ := req.Option("archive").Bool(); archive || cmplvl != gzip.NoCompression {
			if archive && !strings.HasSuffix(outPath, ".tar") {
				outPath += ".tar"
			}
			if cmplvl != gzip.NoCompression {
				outPath += ".gz"
			}
			fmt.Printf("Saving archive to %s\n", outPath)

			file, err := os.Create(outPath)
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
			defer file.Close()

			bar := pb.New(0).SetUnits(pb.U_BYTES)
			bar.Output = os.Stderr
			pbReader := bar.NewProxyReader(outReader)
			bar.Start()
			defer bar.Finish()

			_, err = io.Copy(file, pbReader)
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}

			return
		}

		fmt.Printf("Saving file(s) to %s\n", outPath)

		// TODO: get total length of files
		bar := pb.New(0).SetUnits(pb.U_BYTES)
		bar.Output = os.Stderr

		// wrap the reader with the progress bar proxy reader
		reader := bar.NewProxyReader(outReader)

		bar.Start()
		defer bar.Finish()

		extractor := &tar.Extractor{outPath}
		err = extractor.Extract(reader)
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
		}
	},
}

func getCompressOptions(req cmds.Request) (int, error) {
	cmprs, _, _ := req.Option("compress").Bool()
	cmplvl, cmplvlFound, _ := req.Option("compression-level").Int()
	switch {
	case !cmprs:
		return gzip.NoCompression, nil
	case cmprs && !cmplvlFound:
		return gzip.DefaultCompression, nil
	case cmprs && cmplvlFound && (cmplvl < 1 || cmplvl > 9):
		return gzip.NoCompression, ErrInvalidCompressionLevel
	}
	return gzip.NoCompression, nil
}

func get(ctx context.Context, node *core.IpfsNode, pathToResolve path.Path, compression int) (io.Reader, error) {
	dagnode, err := core.Resolve(ctx, node, pathToResolve)
	if err != nil {
		return nil, err
	}

	return utar.NewReader(pathToResolve, node.DAG, dagnode, compression)
}

// getZip is equivalent to `ipfs getdag $hash | gzip`
func getZip(ctx context.Context, node *core.IpfsNode, pathToResolve path.Path, compression int) (io.Reader, error) {
	dagnode, err := core.Resolve(ctx, node, pathToResolve)
	if err != nil {
		return nil, err
	}

	reader, err := uio.NewDagReader(ctx, dagnode, node.DAG)
	if err != nil {
		return nil, err
	}

	pr, pw := io.Pipe()
	gw, err := gzip.NewWriterLevel(pw, compression)
	if err != nil {
		return nil, err
	}
	bufin := bufio.NewReader(reader)
	go func() {
		_, err := bufin.WriteTo(gw)
		if err != nil {
			log.Error("Fail to compress the stream")
		}
		gw.Close()
		pw.Close()
	}()

	return pr, nil
}
<|endoftext|>


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

// The newrepo command utilizes go-github as a cli tool for
// creating new repositories. It takes an auth token as
// an environment variable and creates the new repo under
// the account affiliated with that token.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/google/go-github/v43/github"
	"golang.org/x/oauth2"
)

var (
	name        = flag.String("name", "", "Name of repo to create in authenticated user's GitHub account.")
	description = flag.String("description", "", "Description of created repo.")
	private     = flag.Bool("private", false, "Will created repo be private.")
)

func main() {
	flag.Parse()
	token := os.Getenv("GITHUB_AUTH_TOKEN")
	if token == "" {
		log.Fatal("Unauthorized: No token present")
	}
	if *name == "" {
		log.Fatal("No name: New repos must be given a name")
	}
	ctx := context.Background()
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
	tc := oauth2.NewClient(ctx, ts)
	client := github.NewClient(tc)

	r := &github.Repository{Name: name, Private: private, Description: description}
	repo, _, err := client.Repositories.Create(ctx, "", r)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Successfully created new repo: %v\n", repo.GetName())
}
<commit_msg>Add example to create repository with an initial commit with empty README.md (#2333)<commit_after>// Copyright 2018 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The newrepo command utilizes go-github as a cli tool for
// creating new repositories. It takes an auth token as
// an environment variable and creates the new repo under
// the account affiliated with that token.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/google/go-github/v43/github"
	"golang.org/x/oauth2"
)

var (
	name        = flag.String("name", "", "Name of repo to create in authenticated user's GitHub account.")
	description = flag.String("description", "", "Description of created repo.")
	private     = flag.Bool("private", false, "Will created repo be private.")
	autoInit    = flag.Bool("auto-init", false, "Pass true to create an initial commit with empty README.")
)

func main() {
	flag.Parse()
	token := os.Getenv("GITHUB_AUTH_TOKEN")
	if token == "" {
		log.Fatal("Unauthorized: No token present")
	}
	if *name == "" {
		log.Fatal("No name: New repos must be given a name")
	}
	ctx := context.Background()
	ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
	tc := oauth2.NewClient(ctx, ts)
	client := github.NewClient(tc)

	r := &github.Repository{Name: name, Private: private, Description: description, AutoInit: autoInit}
	repo, _, err := client.Repositories.Create(ctx, "", r)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Successfully created new repo: %v\n", repo.GetName())
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"github.com/gorilla/mux"
	"net/http"
	"net/url"
	"strconv"
)

const (
	linkSentMsg     = "An account verification email has been sent."
	subscribedMsg   = "Your account is now active!"
	scoreUpdatedMsg = "Your score threshold has been successfully updated!"
	unsubscribedMsg = "You have been successfully unsubscribed."
)

var (
	errInvalidEmail = errors.New("Error: The email address is not valid!")
	errInvalidScore = errors.New("Error: The score field must be a number!")
	errInvalidLink  = errors.New("Error: The link is not valid.")
	errNotFound     = errors.New("Error: The email address you provided is not subscribed to this service!")
	errMinScore     = errors.New("Error: The must select a minimum score of 200 points!")
)

var (
	minScore = 200
)

// errInternal represents an internal server error.
type errInternal struct{ error }

// errMessage represents a meaningful error message, that will be sent to the user.
type errMessage struct{ error }

// Context carries http session information. It will be passed to all HTTP handlers.
// TODO: Include user information, simplifying authentication management.
type Context struct {
	db *Database
}

// newContext creates a new Context, ready to be passed to a HTTP handler.
func newContext() *Context {
	return &Context{
		db: newDatabase(),
	}
}

// handler wraps a custom handler function returning a standard HandlerFunc closure.
func handler(f func(ctx *Context, w http.ResponseWriter, r *http.Request) error) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		Logger.Printf("%s %s %s\n", r.Method, r.URL.Path, r.URL.RawQuery)
		ctx := newContext()
		defer ctx.db.close()

		err := f(ctx, w, r)

		if err == nil {
			return
		}

		// Log the error, and depending on the type, display it to the user.
		Logger.Println(err)
		switch err.(type) {
		case errMessage:
			w.WriteHeader(http.StatusBadRequest)
			writeMessage(err.Error(), w)
		case errInternal:
		default:
			w.WriteHeader(http.StatusInternalServerError)
			writeMessage("Oops! An error ocurred.", w)
		}
	}
}

// setupHandlers registers the HTTP handlers of the app.
func setupHandlers() {
	router := mux.NewRouter()
	router.HandleFunc("/subscribe", handler(SubscribeHandler)).
		Methods("POST")
	router.HandleFunc("/activate", handler(ActivateHandler)).
		Methods("GET")
	router.HandleFunc("/unsubscribe", handler(UnsubscribeHandler)).
		Methods("GET", "POST")

	// serve settings.html static file. index.html works the same way,
	// though it's automatically handled by the root file server handler.
	router.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "./public/settings.html")
	})

	router.PathPrefix("/").Handler(http.FileServer(http.Dir("./public/")))
	http.Handle("/", router)
}

// SubscribeHandler is the HTTP handler for managing new subscriptions; It handles '/subscribe'.
func SubscribeHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	email, ok := parseEmail(r)
	if !ok {
		return errMessage{errInvalidEmail}
	}
	score, ok := parseScore(r)
	if !ok {
		return errMessage{errInvalidScore}
	} else if score < minScore {
		return errMessage{errMinScore}
	}

	q := url.Values{} // Link query parameters.
	u, ok := ctx.db.findUser(email)
	if ok {
		// The user already exists. The score will be added to the query.
		q.Set("score", strconv.Itoa(score))
		u.Token = newToken() // reset user token.
		if err := ctx.db.updateToken(u.Id, u.Token); err != nil {
			return errInternal{err}
		}
	} else {
		u = newUser(email, score)
		if err := ctx.db.upsertUser(u); err != nil {
			return errInternal{err}
		}
	}

	q.Set("email", u.Email)
	q.Set("token", u.Token)
	link := config.Url + "/activate?" + q.Encode()
	go sendVerification(email, link)

	return writeMessage(linkSentMsg, w)
}

// ActivateHandler is the HTTP handler for managing account activations; It handles '/activate'.
func ActivateHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	email, token := r.FormValue("email"), r.FormValue("token")
	score, ok := parseScore(r)
	msg := subscribedMsg
	if ok {
		// We need to update the score too.
		ok = ctx.db.updateScore(email, token, score)
		msg = scoreUpdatedMsg
	} else {
		ok = ctx.db.activate(email, token)
	}

	if ok {
		return writeMessage(msg, w)
	}
	return errMessage{errInvalidLink}
}

// ActivateHandler is the HTTP handler for managing account unsubscriptions; It handles '/unsubscribe'.
func UnsubscribeHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	switch r.Method {
	case "POST":
		email, ok := parseEmail(r)
		u, found := ctx.db.findUser(email)
		if !ok || !found {
			return errMessage{errNotFound}
		}

		u.Token = newToken() // reset user token.
		if err := ctx.db.updateToken(u.Id, u.Token); err != nil {
			return errInternal{err}
		}

		q := url.Values{}
		q.Set("email", u.Email)
		q.Set("token", u.Token)
		link := config.Url + "/unsubscribe?" + q.Encode()
		go sendUnsubscription(email, link)

		return writeMessage(linkSentMsg, w)
	case "GET":
		email, token := r.FormValue("email"), r.FormValue("token")
		if ctx.db.unsubscribe(email, token) {
			return writeMessage(unsubscribedMsg, w)
		}
		return errMessage{errInvalidLink}
	}
	return nil
}

// writeMessage renders a message in the default 'info' template.
func writeMessage(msg string, w http.ResponseWriter) error {
	return useTemplate("info", msg, w)
}

// parseEmail gets the email attribute from the request.
func parseEmail(r *http.Request) (string, bool) {
	email := r.FormValue("email")
	return email, validateAddress(email)
}

// parseScore gets the score attribute from the request.
func parseScore(r *http.Request) (int, bool) {
	score, err := strconv.Atoi(r.FormValue("score"))
	return score, err == nil
}
<commit_msg>Fix typo<commit_after>package main

import (
	"errors"
	"github.com/gorilla/mux"
	"net/http"
	"net/url"
	"strconv"
)

const (
	linkSentMsg     = "An account verification email has been sent."
	subscribedMsg   = "Your account is now active!"
	scoreUpdatedMsg = "Your score threshold has been successfully updated!"
	unsubscribedMsg = "You have been successfully unsubscribed."
)

var (
	errInvalidEmail = errors.New("Error: The email address is not valid!")
	errInvalidScore = errors.New("Error: The score field must be a number!")
	errInvalidLink  = errors.New("Error: The link is not valid.")
	errNotFound     = errors.New("Error: The email address you provided is not subscribed to this service!")
	errMinScore     = errors.New("Error: You must select a minimum score of 200 points!")
)

var (
	minScore = 200
)

// errInternal represents an internal server error.
type errInternal struct{ error }

// errMessage represents a meaningful error message, that will be sent to the user.
type errMessage struct{ error }

// Context carries http session information. It will be passed to all HTTP handlers.
// TODO: Include user information, simplifying authentication management.
type Context struct {
	db *Database
}

// newContext creates a new Context, ready to be passed to a HTTP handler.
func newContext() *Context {
	return &Context{
		db: newDatabase(),
	}
}

// handler wraps a custom handler function returning a standard HandlerFunc closure.
func handler(f func(ctx *Context, w http.ResponseWriter, r *http.Request) error) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		Logger.Printf("%s %s %s\n", r.Method, r.URL.Path, r.URL.RawQuery)
		ctx := newContext()
		defer ctx.db.close()

		err := f(ctx, w, r)

		if err == nil {
			return
		}

		// Log the error, and depending on the type, display it to the user.
		Logger.Println(err)
		switch err.(type) {
		case errMessage:
			w.WriteHeader(http.StatusBadRequest)
			writeMessage(err.Error(), w)
		case errInternal:
		default:
			w.WriteHeader(http.StatusInternalServerError)
			writeMessage("Oops! An error ocurred.", w)
		}
	}
}

// setupHandlers registers the HTTP handlers of the app.
func setupHandlers() {
	router := mux.NewRouter()
	router.HandleFunc("/subscribe", handler(SubscribeHandler)).
		Methods("POST")
	router.HandleFunc("/activate", handler(ActivateHandler)).
		Methods("GET")
	router.HandleFunc("/unsubscribe", handler(UnsubscribeHandler)).
		Methods("GET", "POST")

	// serve settings.html static file. index.html works the same way,
	// though it's automatically handled by the root file server handler.
	router.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, "./public/settings.html")
	})

	router.PathPrefix("/").Handler(http.FileServer(http.Dir("./public/")))
	http.Handle("/", router)
}

// SubscribeHandler is the HTTP handler for managing new subscriptions; It handles '/subscribe'.
func SubscribeHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	email, ok := parseEmail(r)
	if !ok {
		return errMessage{errInvalidEmail}
	}
	score, ok := parseScore(r)
	if !ok {
		return errMessage{errInvalidScore}
	} else if score < minScore {
		return errMessage{errMinScore}
	}

	q := url.Values{} // Link query parameters.
	u, ok := ctx.db.findUser(email)
	if ok {
		// The user already exists. The score will be added to the query.
		q.Set("score", strconv.Itoa(score))
		u.Token = newToken() // reset user token.
		if err := ctx.db.updateToken(u.Id, u.Token); err != nil {
			return errInternal{err}
		}
	} else {
		u = newUser(email, score)
		if err := ctx.db.upsertUser(u); err != nil {
			return errInternal{err}
		}
	}

	q.Set("email", u.Email)
	q.Set("token", u.Token)
	link := config.Url + "/activate?" + q.Encode()
	go sendVerification(email, link)

	return writeMessage(linkSentMsg, w)
}

// ActivateHandler is the HTTP handler for managing account activations; It handles '/activate'.
func ActivateHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	email, token := r.FormValue("email"), r.FormValue("token")
	score, ok := parseScore(r)
	msg := subscribedMsg
	if ok {
		// We need to update the score too.
		ok = ctx.db.updateScore(email, token, score)
		msg = scoreUpdatedMsg
	} else {
		ok = ctx.db.activate(email, token)
	}

	if ok {
		return writeMessage(msg, w)
	}
	return errMessage{errInvalidLink}
}

// ActivateHandler is the HTTP handler for managing account unsubscriptions; It handles '/unsubscribe'.
func UnsubscribeHandler(ctx *Context, w http.ResponseWriter, r *http.Request) error {
	switch r.Method {
	case "POST":
		email, ok := parseEmail(r)
		u, found := ctx.db.findUser(email)
		if !ok || !found {
			return errMessage{errNotFound}
		}

		u.Token = newToken() // reset user token.
		if err := ctx.db.updateToken(u.Id, u.Token); err != nil {
			return errInternal{err}
		}

		q := url.Values{}
		q.Set("email", u.Email)
		q.Set("token", u.Token)
		link := config.Url + "/unsubscribe?" + q.Encode()
		go sendUnsubscription(email, link)

		return writeMessage(linkSentMsg, w)
	case "GET":
		email, token := r.FormValue("email"), r.FormValue("token")
		if ctx.db.unsubscribe(email, token) {
			return writeMessage(unsubscribedMsg, w)
		}
		return errMessage{errInvalidLink}
	}
	return nil
}

// writeMessage renders a message in the default 'info' template.
func writeMessage(msg string, w http.ResponseWriter) error {
	return useTemplate("info", msg, w)
}

// parseEmail gets the email attribute from the request.
func parseEmail(r *http.Request) (string, bool) {
	email := r.FormValue("email")
	return email, validateAddress(email)
}

// parseScore gets the score attribute from the request.
func parseScore(r *http.Request) (int, bool) {
	score, err := strconv.Atoi(r.FormValue("score"))
	return score, err == nil
}
<|endoftext|>


<commit_before>package mockAdapter

import (
	"strconv"
	"sync"

	"github.com/FogCreek/victor/pkg/chat"
)

var (
	nextID         = 0
	nextIDMutex    = &sync.Mutex{}
	defaultUserRet = &chat.BaseUser{
		UserName:  "Fake User",
		UserID:    "UFakeUser",
		UserIsBot: false,
		UserEmail: "fake@example.com",
	}
	defaultChannelRet = &chat.BaseChannel{
		Name: "Fake Channel",
		ID:   "CFakeChannel",
	}
)

// init Registers the mockAdapter with the victor framework under the chat
// adapter name "mockAdapter".
func init() {
	chat.Register("mockAdapter", func(r chat.Robot) chat.Adapter {
		nextIDMutex.Lock()
		id := nextID
		nextID++
		nextIDMutex.Unlock()
		return &MockChatAdapter{
			robot:              r,
			id:                 strconv.Itoa(id),
			Sent:               make([]MockMessagePair, 0, 10),
			SentPublic:         make([]MockMessagePair, 0, 10),
			SentDirect:         make([]MockMessagePair, 0, 10),
			IsPotentialUserRet: true,
			NormalizeUserIDRet: "",
			UserRet:            defaultUserRet,
			AllUsersRet:        []chat.User{defaultUserRet},
			PublicChannelsRet:  []chat.Channel{defaultChannelRet},
		}
	})
}

// MockChatAdapter provides an "empty" chat adapter which can be used to test
// victor and/or victor handler functions. It stores all sent messages to an
// exported array and allows certain function's returned values (GetUser,
// IsPotentialUser, etc.) to be set.
type MockChatAdapter struct {
	id    string
	robot chat.Robot
	Sent,
	SentPublic,
	SentDirect []MockMessagePair
	UserRet            chat.User
	AllUsersRet        []chat.User
	PublicChannelsRet  []chat.Channel
	IsPotentialUserRet bool
	NormalizeUserIDRet string
}

// Clear clears the contents of the "Sent" array.
func (m *MockChatAdapter) Clear() {
	m.Sent = make([]MockMessagePair, 0, 10)
	m.SentPublic = make([]MockMessagePair, 0, 10)
	m.SentDirect = make([]MockMessagePair, 0, 10)
}

// Receive mocks a message being received by the chat adapter.
func (m *MockChatAdapter) Receive(mp chat.Message) {
	m.robot.Receive(mp)
}

// Run does nothing as the mockAdapter does not connect to anything.
func (m *MockChatAdapter) Run() {
	return
}

// Send stores the given channelID and text to the exported array "Sent" as
// a MockMessagePair.
func (m *MockChatAdapter) Send(channelID, text string) {
	m.Sent = append(m.Sent, MockMessagePair{
		text:      text,
		channelID: channelID,
		isDirect:  false,
	})
	m.SentPublic = append(m.SentPublic, MockMessagePair{
		text:      text,
		channelID: channelID,
		isDirect:  false,
	})
}

// SendDirectMessage stores the given userID and text to the exported array
// "Sent" as a MockMessagePair with the "IsDirect" flag set to true.
func (m *MockChatAdapter) SendDirectMessage(userID, text string) {
	m.Sent = append(m.Sent, MockMessagePair{
		text:     text,
		userID:   userID,
		isDirect: true,
	})
	m.SentDirect = append(m.SentDirect, MockMessagePair{
		text:     text,
		userID:   userID,
		isDirect: true,
	})
}

// SendTyping does nothing.
func (m *MockChatAdapter) SendTyping(channelID string) {
	return
}

// Stop does nothing.
func (m *MockChatAdapter) Stop() {
	return
}

// ID returns the mockAdapter's ID.
func (m *MockChatAdapter) ID() string {
	return m.id
}

// GetUser returns the mockAdapter's set "UserRet" property which can be
// set to any chat.User instance (default value has full name "Fake User").
func (m *MockChatAdapter) GetUser(string) chat.User {
	return m.UserRet
}

func (m *MockChatAdapter) GetAllUsers() []chat.User {
	return m.AllUsersRet
}

func (m *MockChatAdapter) GetPublicChannels() []Channel {
	return m.PublicChannelsRet
}

// IsPotentialUser returns the mockAdapter's set "IsPotentialUserRet" property
// which has a default value of "true".
func (m *MockChatAdapter) IsPotentialUser(string) bool {
	return m.IsPotentialUserRet
}

func (m *MockChatAdapter) NormalizeUserID(id string) string {
	if m.NormalizeUserIDRet == "" {
		return id
	}
	return m.NormalizeUserIDRet
}

// MockMessagePair is used to store messages that are sent by chat handlers to
// the mockAdapter instance.
type MockMessagePair struct {
	channelID,
	userID,
	text string
	isDirect bool
}

// ChannelID returns the id of the channel that the sent message was intended
// for. If this is a direct message then this will not be set.
func (mp *MockMessagePair) ChannelID() string {
	return mp.channelID
}

// Text returns the message's full text.
func (mp *MockMessagePair) Text() string {
	return mp.text
}

// UserID returns the id of the user that the message was intended for. This
// will not be sent unless it is a direct message.
func (mp *MockMessagePair) UserID() string {
	return mp.userID
}

// IsDirect returns true if the message is direct and false otherwise. The
// output of this can be used to determine whether ChannelID() or UserID are
// relavent to this message.
func (mp *MockMessagePair) IsDirect() bool {
	return mp.isDirect
}
<commit_msg>Fixed mock<commit_after>package mockAdapter

import (
	"strconv"
	"sync"

	"github.com/FogCreek/victor/pkg/chat"
)

var (
	nextID         = 0
	nextIDMutex    = &sync.Mutex{}
	defaultUserRet = &chat.BaseUser{
		UserName:  "Fake User",
		UserID:    "UFakeUser",
		UserIsBot: false,
		UserEmail: "fake@example.com",
	}
	defaultChannelRet = &chat.BaseChannel{
		ChannelName: "Fake Channel",
		ChannelID:   "CFakeChannel",
	}
)

// init Registers the mockAdapter with the victor framework under the chat
// adapter name "mockAdapter".
func init() {
	chat.Register("mockAdapter", func(r chat.Robot) chat.Adapter {
		nextIDMutex.Lock()
		id := nextID
		nextID++
		nextIDMutex.Unlock()
		return &MockChatAdapter{
			robot:              r,
			id:                 strconv.Itoa(id),
			Sent:               make([]MockMessagePair, 0, 10),
			SentPublic:         make([]MockMessagePair, 0, 10),
			SentDirect:         make([]MockMessagePair, 0, 10),
			IsPotentialUserRet: true,
			NormalizeUserIDRet: "",
			UserRet:            defaultUserRet,
			AllUsersRet:        []chat.User{defaultUserRet},
			PublicChannelsRet:  []chat.Channel{defaultChannelRet},
		}
	})
}

// MockChatAdapter provides an "empty" chat adapter which can be used to test
// victor and/or victor handler functions. It stores all sent messages to an
// exported array and allows certain function's returned values (GetUser,
// IsPotentialUser, etc.) to be set.
type MockChatAdapter struct {
	id    string
	robot chat.Robot
	Sent,
	SentPublic,
	SentDirect []MockMessagePair
	UserRet            chat.User
	AllUsersRet        []chat.User
	PublicChannelsRet  []chat.Channel
	IsPotentialUserRet bool
	NormalizeUserIDRet string
}

// Clear clears the contents of the "Sent" array.
func (m *MockChatAdapter) Clear() {
	m.Sent = make([]MockMessagePair, 0, 10)
	m.SentPublic = make([]MockMessagePair, 0, 10)
	m.SentDirect = make([]MockMessagePair, 0, 10)
}

// Receive mocks a message being received by the chat adapter.
func (m *MockChatAdapter) Receive(mp chat.Message) {
	m.robot.Receive(mp)
}

// Run does nothing as the mockAdapter does not connect to anything.
func (m *MockChatAdapter) Run() {
	return
}

// Send stores the given channelID and text to the exported array "Sent" as
// a MockMessagePair.
func (m *MockChatAdapter) Send(channelID, text string) {
	m.Sent = append(m.Sent, MockMessagePair{
		text:      text,
		channelID: channelID,
		isDirect:  false,
	})
	m.SentPublic = append(m.SentPublic, MockMessagePair{
		text:      text,
		channelID: channelID,
		isDirect:  false,
	})
}

// SendDirectMessage stores the given userID and text to the exported array
// "Sent" as a MockMessagePair with the "IsDirect" flag set to true.
func (m *MockChatAdapter) SendDirectMessage(userID, text string) {
	m.Sent = append(m.Sent, MockMessagePair{
		text:     text,
		userID:   userID,
		isDirect: true,
	})
	m.SentDirect = append(m.SentDirect, MockMessagePair{
		text:     text,
		userID:   userID,
		isDirect: true,
	})
}

// SendTyping does nothing.
func (m *MockChatAdapter) SendTyping(channelID string) {
	return
}

// Stop does nothing.
func (m *MockChatAdapter) Stop() {
	return
}

// ID returns the mockAdapter's ID.
func (m *MockChatAdapter) ID() string {
	return m.id
}

// GetUser returns the mockAdapter's set "UserRet" property which can be
// set to any chat.User instance (default value has full name "Fake User").
func (m *MockChatAdapter) GetUser(string) chat.User {
	return m.UserRet
}

func (m *MockChatAdapter) GetAllUsers() []chat.User {
	return m.AllUsersRet
}

func (m *MockChatAdapter) GetPublicChannels() []chat.Channel {
	return m.PublicChannelsRet
}

// IsPotentialUser returns the mockAdapter's set "IsPotentialUserRet" property
// which has a default value of "true".
func (m *MockChatAdapter) IsPotentialUser(string) bool {
	return m.IsPotentialUserRet
}

func (m *MockChatAdapter) NormalizeUserID(id string) string {
	if m.NormalizeUserIDRet == "" {
		return id
	}
	return m.NormalizeUserIDRet
}

// MockMessagePair is used to store messages that are sent by chat handlers to
// the mockAdapter instance.
type MockMessagePair struct {
	channelID,
	userID,
	text string
	isDirect bool
}

// ChannelID returns the id of the channel that the sent message was intended
// for. If this is a direct message then this will not be set.
func (mp *MockMessagePair) ChannelID() string {
	return mp.channelID
}

// Text returns the message's full text.
func (mp *MockMessagePair) Text() string {
	return mp.text
}

// UserID returns the id of the user that the message was intended for. This
// will not be sent unless it is a direct message.
func (mp *MockMessagePair) UserID() string {
	return mp.userID
}

// IsDirect returns true if the message is direct and false otherwise. The
// output of this can be used to determine whether ChannelID() or UserID are
// relavent to this message.
func (mp *MockMessagePair) IsDirect() bool {
	return mp.isDirect
}
<|endoftext|>