<commit_before>package test

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"net/http"
	"testing"
	"time"

	"github.com/eleniums/gohost"
	"github.com/eleniums/gohost/examples/test"
	"google.golang.org/grpc"

	pb "github.com/eleniums/gohost/examples/test/proto"
	assert "github.com/stretchr/testify/require"
)

func Test_Hoster_ListenAndServe_HTTP_Successful(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	expectedValue := "test"

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	httpReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%v/v1/echo?value=%v", httpAddr, expectedValue), nil)
	assert.NoError(t, err)
	doResp, err := httpClient.Do(httpReq)
	assert.NoError(t, err)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.EchoResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.Equal(t, expectedValue, httpResp.Echo)
}

func Test_Hoster_ListenAndServe_HTTP_WithTLS(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	expectedValue := "test"

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.CertFile = "../testdata/test.crt"
	hoster.KeyFile = "../testdata/test.key"
	hoster.InsecureSkipVerify = true

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}
	httpReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://%v/v1/echo?value=%v", httpAddr, expectedValue), nil)
	assert.NoError(t, err)
	doResp, err := httpClient.Do(httpReq)
	assert.NoError(t, err)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.EchoResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.Equal(t, expectedValue, httpResp.Echo)
}

func Test_Hoster_ListenAndServe_HTTP_EmptyAddress(t *testing.T) {
	// arrange
	hoster := gohost.NewHoster()

	hoster.HTTPAddr = ""
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	// act - start the service
	err := hoster.ListenAndServe()

	// assert
	assert.Error(t, err)
}

func Test_Hoster_ListenAndServe_HTTP_InvalidAddress(t *testing.T) {
	// arrange
	hoster := gohost.NewHoster()

	hoster.HTTPAddr = "badaddress"
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	// act - start the service
	err := hoster.ListenAndServe()

	// assert
	assert.Error(t, err)
}

func Test_Hoster_ListenAndServe_HTTP_InvalidCertFile(t *testing.T) {
	// arrange
	hoster := gohost.NewHoster()
	httpAddr := getAddr(t)

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.CertFile = "../testdata/badcert.crt"
	hoster.KeyFile = "../testdata/test.key"

	// act - start the service
	err := hoster.ListenAndServe()

	// assert
	assert.Error(t, err)
}

func Test_Hoster_ListenAndServe_HTTP_InvalidKeyFile(t *testing.T) {
	// arrange
	hoster := gohost.NewHoster()
	httpAddr := getAddr(t)

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.CertFile = "../testdata/test.crt"
	hoster.KeyFile = "../testdata/badkey.key"

	// act - start the service
	err := hoster.ListenAndServe()

	// assert
	assert.Error(t, err)
}

func Test_Hoster_ListenAndServe_HTTP_MaxRecvMsgSize_Pass(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	largeValue := string(make([]byte, largeMessageLength))

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.MaxRecvMsgSize = math.MaxInt32

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	httpReq := pb.SendRequest{
		Value: largeValue,
	}
	payload, err := json.Marshal(&httpReq)
	assert.NoError(t, err)
	postReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%v/v1/send", httpAddr), bytes.NewBuffer(payload))
	assert.NoError(t, err)
	doResp, err := httpClient.Do(postReq)
	assert.NoError(t, err)
	assert.Equal(t, 200, doResp.StatusCode)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.TestResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.True(t, httpResp.Success)
}

func Test_Hoster_ListenAndServe_HTTP_MaxRecvMsgSize_Fail(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	largeValue := string(make([]byte, largeMessageLength))

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.MaxRecvMsgSize = 1

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	httpReq := pb.SendRequest{
		Value: largeValue,
	}
	payload, err := json.Marshal(&httpReq)
	assert.NoError(t, err)
	postReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%v/v1/send", httpAddr), bytes.NewBuffer(payload))
	assert.NoError(t, err)
	doResp, err := httpClient.Do(postReq)
	assert.NoError(t, err)
	assert.NotEqual(t, 200, doResp.StatusCode)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.TestResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.False(t, httpResp.Success)
}

func Test_Hoster_ListenAndServe_HTTP_MaxSendMsgSize_Pass(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.MaxSendMsgSize = math.MaxInt32

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	postReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%v/v1/large?length=%v", httpAddr, largeMessageLength), nil)
	assert.NoError(t, err)
	doResp, err := httpClient.Do(postReq)
	assert.NoError(t, err)
	assert.Equal(t, 200, doResp.StatusCode)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.EchoResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.Equal(t, largeMessageLength, len(httpResp.Echo))
}

func Test_Hoster_ListenAndServe_HTTP_MaxSendMsgSize_Fail(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.MaxSendMsgSize = 1

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	postReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%v/v1/large?length=%v", httpAddr, largeMessageLength), nil)
	assert.NoError(t, err)
	doResp, err := httpClient.Do(postReq)
	assert.NoError(t, err)
	assert.NotEqual(t, 200, doResp.StatusCode)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.EchoResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.Empty(t, httpResp.Echo)
}
<commit_msg>Add some extra time to wait for test service startup<commit_after>package test

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"net/http"
	"testing"
	"time"

	"github.com/eleniums/gohost"
	"github.com/eleniums/gohost/examples/test"
	"google.golang.org/grpc"

	pb "github.com/eleniums/gohost/examples/test/proto"
	assert "github.com/stretchr/testify/require"
)

func Test_Hoster_ListenAndServe_HTTP_Successful(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	expectedValue := "test"

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	httpReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%v/v1/echo?value=%v", httpAddr, expectedValue), nil)
	assert.NoError(t, err)
	doResp, err := httpClient.Do(httpReq)
	assert.NoError(t, err)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.EchoResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.Equal(t, expectedValue, httpResp.Echo)
}

func Test_Hoster_ListenAndServe_HTTP_WithTLS(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	expectedValue := "test"

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.CertFile = "../testdata/test.crt"
	hoster.KeyFile = "../testdata/test.key"
	hoster.InsecureSkipVerify = true

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start (with a little extra time since this is slower than others)
	time.Sleep(serviceStartDelay + time.Second)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
		},
	}
	httpReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://%v/v1/echo?value=%v", httpAddr, expectedValue), nil)
	assert.NoError(t, err)
	doResp, err := httpClient.Do(httpReq)
	assert.NoError(t, err)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.EchoResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.Equal(t, expectedValue, httpResp.Echo)
}

func Test_Hoster_ListenAndServe_HTTP_EmptyAddress(t *testing.T) {
	// arrange
	hoster := gohost.NewHoster()

	hoster.HTTPAddr = ""
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	// act - start the service
	err := hoster.ListenAndServe()

	// assert
	assert.Error(t, err)
}

func Test_Hoster_ListenAndServe_HTTP_InvalidAddress(t *testing.T) {
	// arrange
	hoster := gohost.NewHoster()

	hoster.HTTPAddr = "badaddress"
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	// act - start the service
	err := hoster.ListenAndServe()

	// assert
	assert.Error(t, err)
}

func Test_Hoster_ListenAndServe_HTTP_InvalidCertFile(t *testing.T) {
	// arrange
	hoster := gohost.NewHoster()
	httpAddr := getAddr(t)

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.CertFile = "../testdata/badcert.crt"
	hoster.KeyFile = "../testdata/test.key"

	// act - start the service
	err := hoster.ListenAndServe()

	// assert
	assert.Error(t, err)
}

func Test_Hoster_ListenAndServe_HTTP_InvalidKeyFile(t *testing.T) {
	// arrange
	hoster := gohost.NewHoster()
	httpAddr := getAddr(t)

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.CertFile = "../testdata/test.crt"
	hoster.KeyFile = "../testdata/badkey.key"

	// act - start the service
	err := hoster.ListenAndServe()

	// assert
	assert.Error(t, err)
}

func Test_Hoster_ListenAndServe_HTTP_MaxRecvMsgSize_Pass(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	largeValue := string(make([]byte, largeMessageLength))

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.MaxRecvMsgSize = math.MaxInt32

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	httpReq := pb.SendRequest{
		Value: largeValue,
	}
	payload, err := json.Marshal(&httpReq)
	assert.NoError(t, err)
	postReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%v/v1/send", httpAddr), bytes.NewBuffer(payload))
	assert.NoError(t, err)
	doResp, err := httpClient.Do(postReq)
	assert.NoError(t, err)
	assert.Equal(t, 200, doResp.StatusCode)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.TestResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.True(t, httpResp.Success)
}

func Test_Hoster_ListenAndServe_HTTP_MaxRecvMsgSize_Fail(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	largeValue := string(make([]byte, largeMessageLength))

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.MaxRecvMsgSize = 1

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	httpReq := pb.SendRequest{
		Value: largeValue,
	}
	payload, err := json.Marshal(&httpReq)
	assert.NoError(t, err)
	postReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("http://%v/v1/send", httpAddr), bytes.NewBuffer(payload))
	assert.NoError(t, err)
	doResp, err := httpClient.Do(postReq)
	assert.NoError(t, err)
	assert.NotEqual(t, 200, doResp.StatusCode)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.TestResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.False(t, httpResp.Success)
}

func Test_Hoster_ListenAndServe_HTTP_MaxSendMsgSize_Pass(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.MaxSendMsgSize = math.MaxInt32

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	postReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%v/v1/large?length=%v", httpAddr, largeMessageLength), nil)
	assert.NoError(t, err)
	doResp, err := httpClient.Do(postReq)
	assert.NoError(t, err)
	assert.Equal(t, 200, doResp.StatusCode)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.EchoResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.Equal(t, largeMessageLength, len(httpResp.Echo))
}

func Test_Hoster_ListenAndServe_HTTP_MaxSendMsgSize_Fail(t *testing.T) {
	// arrange
	service := test.NewService()
	httpAddr := getAddr(t)
	grpcAddr := getAddr(t)

	hoster := gohost.NewHoster()
	hoster.GRPCAddr = grpcAddr
	hoster.RegisterGRPCServer(func(s *grpc.Server) {
		pb.RegisterTestServiceServer(s, service)
	})

	hoster.HTTPAddr = httpAddr
	hoster.RegisterHTTPGateway(pb.RegisterTestServiceHandlerFromEndpoint)

	hoster.MaxSendMsgSize = 1

	// act - start the service
	go hoster.ListenAndServe()

	// make sure service has time to start
	time.Sleep(serviceStartDelay)

	// call the service at the HTTP endpoint
	httpClient := http.Client{
		Timeout: httpClientTimeout,
	}
	postReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%v/v1/large?length=%v", httpAddr, largeMessageLength), nil)
	assert.NoError(t, err)
	doResp, err := httpClient.Do(postReq)
	assert.NoError(t, err)
	assert.NotEqual(t, 200, doResp.StatusCode)
	body, err := ioutil.ReadAll(doResp.Body)
	assert.NoError(t, err)
	httpResp := pb.EchoResponse{}
	err = json.Unmarshal(body, &httpResp)

	// assert
	assert.NoError(t, err)
	assert.NotNil(t, httpResp)
	assert.Empty(t, httpResp.Echo)
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"github.com/github/git-media/gitmedia"
	"github.com/github/git-media/pointer"
	"github.com/spf13/cobra"
	"io"
	"os"
	"path/filepath"
)

var (
	smudgeInfo = false
	smudgeCmd  = &cobra.Command{
		Use:   "smudge",
		Short: "Implements the Git smudge filter",
		Run:   smudgeCommand,
	}
)

func smudgeCommand(cmd *cobra.Command, args []string) {
	gitmedia.InstallHooks()

	b := &bytes.Buffer{}
	r := io.TeeReader(os.Stdin, b)

	ptr, err := pointer.Decode(r)
	if err != nil {
		mr := io.MultiReader(b, os.Stdin)
		io.Copy(os.Stdout, mr)
		return
	}

	if smudgeInfo {
		localPath, err := gitmedia.LocalMediaPath(ptr.Oid)
		if err != nil {
			Exit(err.Error())
		}

		stat, err := os.Stat(localPath)
		if err != nil {
			Print("%d --", ptr.Size)
		} else {
			Print("%d %s", stat.Size(), localPath)
		}
		return
	}

	filename := smudgeFilename(args, err)
	cb, file, err := gitmedia.CopyCallbackFile("smudge", filename, 1, 1)
	if err != nil {
		Error(err.Error())
	}

	err = ptr.Smudge(os.Stdout, cb)
	if file != nil {
		file.Close()
	}

	if err != nil {
		ptr.Encode(os.Stdout)
		LoggedError(err, "Error accessing media: %s (%s)", filename, ptr.Oid)
	}
}

func smudgeFilename(args []string, err error) string {
	if len(args) > 0 {
		return args[0]
	}

	if smudgeErr, ok := err.(*pointer.SmudgeError); ok {
		return filepath.Base(smudgeErr.Filename)
	}

	return "<unknown file>"
}

func init() {
	smudgeCmd.Flags().BoolVarP(&smudgeInfo, "info", "i", false, "whatever")
	RootCmd.AddCommand(smudgeCmd)
}
<commit_msg>ンンー ンンンン ンーンン<commit_after>package commands

import (
	"bytes"
	"github.com/github/git-media/gitmedia"
	"github.com/github/git-media/pointer"
	"github.com/spf13/cobra"
	"io"
	"os"
	"path/filepath"
)

var (
	smudgeInfo = false
	smudgeCmd  = &cobra.Command{
		Use:   "smudge",
		Short: "Implements the Git smudge filter",
		Run:   smudgeCommand,
	}
)

func smudgeCommand(cmd *cobra.Command, args []string) {
	gitmedia.InstallHooks()

	b := &bytes.Buffer{}
	r := io.TeeReader(os.Stdin, b)

	ptr, err := pointer.Decode(r)
	if err != nil {
		mr := io.MultiReader(b, os.Stdin)
		_, err := io.Copy(os.Stdout, mr)
		if err != nil {
			Panic(err, "Error writing data to stdout:")
		}
		return
	}

	if smudgeInfo {
		localPath, err := gitmedia.LocalMediaPath(ptr.Oid)
		if err != nil {
			Exit(err.Error())
		}

		stat, err := os.Stat(localPath)
		if err != nil {
			Print("%d --", ptr.Size)
		} else {
			Print("%d %s", stat.Size(), localPath)
		}
		return
	}

	filename := smudgeFilename(args, err)
	cb, file, err := gitmedia.CopyCallbackFile("smudge", filename, 1, 1)
	if err != nil {
		Error(err.Error())
	}

	err = ptr.Smudge(os.Stdout, cb)
	if file != nil {
		file.Close()
	}

	if err != nil {
		ptr.Encode(os.Stdout)
		LoggedError(err, "Error accessing media: %s (%s)", filename, ptr.Oid)
	}
}

func smudgeFilename(args []string, err error) string {
	if len(args) > 0 {
		return args[0]
	}

	if smudgeErr, ok := err.(*pointer.SmudgeError); ok {
		return filepath.Base(smudgeErr.Filename)
	}

	return "<unknown file>"
}

func init() {
	smudgeCmd.Flags().BoolVarP(&smudgeInfo, "info", "i", false, "whatever")
	RootCmd.AddCommand(smudgeCmd)
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"io"
	"os"
	"runtime"

	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/filepathfilter"
	"github.com/git-lfs/git-lfs/lfs"
	"github.com/git-lfs/git-lfs/localstorage"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tools/humanize"
	"github.com/spf13/cobra"
)

var (
	// smudgeSkip is a command-line flag belonging to the "git-lfs smudge"
	// command specifying whether to skip the smudge process.
	smudgeSkip = false
)

// smudge smudges the given `*lfs.Pointer`, "ptr", and writes its objects
// contents to the `io.Writer`, "to".
//
// If the encoded LFS pointer is not parse-able as a pointer, the contents of
// that file will instead be spooled to a temporary location on disk and then
// copied out back to Git. If the pointer file is empty, an empty file will be
// written with no error.
//
// If the smudged object did not "pass" the include and exclude filterset, it
// will not be downloaded, and the object will remain a pointer on disk, as if
// the smudge filter had not been applied at all.
//
// Any errors encountered along the way will be returned immediately if they
// were non-fatal, otherwise execution will halt and the process will be
// terminated by using the `commands.Panic()` func.
func smudge(to io.Writer, from io.Reader, filename string, skip bool, filter *filepathfilter.Filter) (int64, error) {
	ptr, pbuf, perr := lfs.DecodeFrom(from)
	if perr != nil {
		n, err := tools.Spool(to, pbuf, localstorage.Objects().TempDir)
		if err != nil {
			return 0, errors.Wrap(err, perr.Error())
		}

		if n != 0 {
			return 0, errors.NewNotAPointerError(errors.Errorf(
				"Unable to parse pointer at: %q", filename,
			))
		}
		return 0, nil
	}

	lfs.LinkOrCopyFromReference(ptr.Oid, ptr.Size)
	cb, file, err := lfs.CopyCallbackFile("smudge", filename, 1, 1)
	if err != nil {
		return 0, err
	}

	download := !skip
	if download {
		download = filter.Allows(filename)
	}

	n, err := ptr.Smudge(to, filename, download, getTransferManifest(), cb)
	if file != nil {
		file.Close()
	}

	if err != nil {
		ptr.Encode(to)
		// Download declined error is ok to skip if we weren't requesting download
		if !(errors.IsDownloadDeclinedError(err) && !download) {
			var oid string = ptr.Oid
			if len(oid) >= 7 {
				oid = oid[:7]
			}

			LoggedError(err, "Error downloading object: %s (%s): %s", filename, oid, err)
			if !cfg.SkipDownloadErrors() {
				os.Exit(2)
			}
		}
	}

	return n, nil
}

func smudgeCommand(cmd *cobra.Command, args []string) {
	requireStdin("This command should be run by the Git 'smudge' filter")
	lfs.InstallHooks(false)

	if !smudgeSkip && cfg.Os.Bool("GIT_LFS_SKIP_SMUDGE", false) {
		smudgeSkip = true
	}
	filter := filepathfilter.New(cfg.FetchIncludePaths(), cfg.FetchExcludePaths())

	if n, err := smudge(os.Stdout, os.Stdin, smudgeFilename(args), smudgeSkip, filter); err != nil {
		if errors.IsNotAPointerError(err) {
			fmt.Fprintln(os.Stderr, err.Error())
		} else {
			Error(err.Error())
		}
	} else if possiblyMalformedSmudge(n) {
		fmt.Fprintln(os.Stderr, "Possibly malformed smudge on Windows: see `git lfs help smudge` for more info.")
	}
}

func smudgeFilename(args []string) string {
	if len(args) > 0 {
		return args[0]
	}
	return "<unknown file>"
}

func possiblyMalformedSmudge(n int64) bool {
	return n > 4*humanize.Gigabyte && runtime.GOOS == "windows"
}

func init() {
	RegisterCommand("smudge", smudgeCommand, func(cmd *cobra.Command) {
		cmd.Flags().BoolVarP(&smudgeSkip, "skip", "s", false, "")
	})
}
<commit_msg>commands/smudge: ensure that GIT_LFS_PROGRESS direction is 'download'<commit_after>package commands

import (
	"fmt"
	"io"
	"os"
	"runtime"

	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/filepathfilter"
	"github.com/git-lfs/git-lfs/lfs"
	"github.com/git-lfs/git-lfs/localstorage"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/git-lfs/git-lfs/tools/humanize"
	"github.com/spf13/cobra"
)

var (
	// smudgeSkip is a command-line flag belonging to the "git-lfs smudge"
	// command specifying whether to skip the smudge process.
	smudgeSkip = false
)

// smudge smudges the given `*lfs.Pointer`, "ptr", and writes its objects
// contents to the `io.Writer`, "to".
//
// If the encoded LFS pointer is not parse-able as a pointer, the contents of
// that file will instead be spooled to a temporary location on disk and then
// copied out back to Git. If the pointer file is empty, an empty file will be
// written with no error.
//
// If the smudged object did not "pass" the include and exclude filterset, it
// will not be downloaded, and the object will remain a pointer on disk, as if
// the smudge filter had not been applied at all.
//
// Any errors encountered along the way will be returned immediately if they
// were non-fatal, otherwise execution will halt and the process will be
// terminated by using the `commands.Panic()` func.
func smudge(to io.Writer, from io.Reader, filename string, skip bool, filter *filepathfilter.Filter) (int64, error) {
	ptr, pbuf, perr := lfs.DecodeFrom(from)
	if perr != nil {
		n, err := tools.Spool(to, pbuf, localstorage.Objects().TempDir)
		if err != nil {
			return 0, errors.Wrap(err, perr.Error())
		}

		if n != 0 {
			return 0, errors.NewNotAPointerError(errors.Errorf(
				"Unable to parse pointer at: %q", filename,
			))
		}
		return 0, nil
	}

	lfs.LinkOrCopyFromReference(ptr.Oid, ptr.Size)
	cb, file, err := lfs.CopyCallbackFile("download", filename, 1, 1)
	if err != nil {
		return 0, err
	}

	download := !skip
	if download {
		download = filter.Allows(filename)
	}

	n, err := ptr.Smudge(to, filename, download, getTransferManifest(), cb)
	if file != nil {
		file.Close()
	}

	if err != nil {
		ptr.Encode(to)
		// Download declined error is ok to skip if we weren't requesting download
		if !(errors.IsDownloadDeclinedError(err) && !download) {
			var oid string = ptr.Oid
			if len(oid) >= 7 {
				oid = oid[:7]
			}

			LoggedError(err, "Error downloading object: %s (%s): %s", filename, oid, err)
			if !cfg.SkipDownloadErrors() {
				os.Exit(2)
			}
		}
	}

	return n, nil
}

func smudgeCommand(cmd *cobra.Command, args []string) {
	requireStdin("This command should be run by the Git 'smudge' filter")
	lfs.InstallHooks(false)

	if !smudgeSkip && cfg.Os.Bool("GIT_LFS_SKIP_SMUDGE", false) {
		smudgeSkip = true
	}
	filter := filepathfilter.New(cfg.FetchIncludePaths(), cfg.FetchExcludePaths())

	if n, err := smudge(os.Stdout, os.Stdin, smudgeFilename(args), smudgeSkip, filter); err != nil {
		if errors.IsNotAPointerError(err) {
			fmt.Fprintln(os.Stderr, err.Error())
		} else {
			Error(err.Error())
		}
	} else if possiblyMalformedSmudge(n) {
		fmt.Fprintln(os.Stderr, "Possibly malformed smudge on Windows: see `git lfs help smudge` for more info.")
	}
}

func smudgeFilename(args []string) string {
	if len(args) > 0 {
		return args[0]
	}
	return "<unknown file>"
}

func possiblyMalformedSmudge(n int64) bool {
	return n > 4*humanize.Gigabyte && runtime.GOOS == "windows"
}

func init() {
	RegisterCommand("smudge", smudgeCommand, func(cmd *cobra.Command) {
		cmd.Flags().BoolVarP(&smudgeSkip, "skip", "s", false, "")
	})
}
<|endoftext|>


<commit_before>package zerolog

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/mattn/go-colorable"
)

const (
	colorBlack = iota + 30
	colorRed
	colorGreen
	colorYellow
	colorBlue
	colorMagenta
	colorCyan
	colorWhite

	colorBold     = 1
	colorDarkGray = 90
)

var (
	consoleBufPool = sync.Pool{
		New: func() interface{} {
			return bytes.NewBuffer(make([]byte, 0, 100))
		},
	}
)

const (
	consoleDefaultTimeFormat = time.Kitchen
)

// Formatter transforms the input into a formatted string.
type Formatter func(interface{}) string

// ConsoleWriter parses the JSON input and writes it in an
// (optionally) colorized, human-friendly format to Out.
type ConsoleWriter struct {
	// Out is the output destination.
	Out io.Writer

	// NoColor disables the colorized output.
	NoColor bool

	// TimeFormat specifies the format for timestamp in output.
	TimeFormat string

	// PartsOrder defines the order of parts in output.
	PartsOrder []string

	// PartsExclude defines parts to not display in output.
	PartsExclude []string

	// FieldsExclude defines contextual fields to not display in output.
	FieldsExclude []string

	FormatTimestamp     Formatter
	FormatLevel         Formatter
	FormatCaller        Formatter
	FormatMessage       Formatter
	FormatFieldName     Formatter
	FormatFieldValue    Formatter
	FormatErrFieldName  Formatter
	FormatErrFieldValue Formatter

	FormatExtra func(map[string]interface{}, *bytes.Buffer) error
}

// NewConsoleWriter creates and initializes a new ConsoleWriter.
func NewConsoleWriter(options ...func(w *ConsoleWriter)) ConsoleWriter {
	w := ConsoleWriter{
		Out:        os.Stdout,
		TimeFormat: consoleDefaultTimeFormat,
		PartsOrder: consoleDefaultPartsOrder(),
	}

	for _, opt := range options {
		opt(&w)
	}

	// Fix color on Windows
	if w.Out == os.Stdout || w.Out == os.Stderr {
		w.Out = colorable.NewColorable(w.Out.(*os.File))
	}

	return w
}

// Write transforms the JSON input with formatters and appends to w.Out.
func (w ConsoleWriter) Write(p []byte) (n int, err error) {
	// Fix color on Windows
	if w.Out == os.Stdout || w.Out == os.Stderr {
		w.Out = colorable.NewColorable(w.Out.(*os.File))
	}

	if w.PartsOrder == nil {
		w.PartsOrder = consoleDefaultPartsOrder()
	}

	var buf = consoleBufPool.Get().(*bytes.Buffer)
	defer func() {
		buf.Reset()
		consoleBufPool.Put(buf)
	}()

	var evt map[string]interface{}
	p = decodeIfBinaryToBytes(p)
	d := json.NewDecoder(bytes.NewReader(p))
	d.UseNumber()
	err = d.Decode(&evt)
	if err != nil {
		return n, fmt.Errorf("cannot decode event: %s", err)
	}

	for _, p := range w.PartsOrder {
		w.writePart(buf, evt, p)
	}

	w.writeFields(evt, buf)

	if w.FormatExtra != nil {
		err = w.FormatExtra(evt, buf)
		if err != nil {
			return n, err
		}
	}

	err = buf.WriteByte('\n')
	if err != nil {
		return n, err
	}

	_, err = buf.WriteTo(w.Out)
	return len(p), err
}

// writeFields appends formatted key-value pairs to buf.
func (w ConsoleWriter) writeFields(evt map[string]interface{}, buf *bytes.Buffer) {
	var fields = make([]string, 0, len(evt))
	for field := range evt {
		var isExcluded bool
		for _, excluded := range w.FieldsExclude {
			if field == excluded {
				isExcluded = true
				break
			}
		}
		if isExcluded {
			continue
		}

		switch field {
		case LevelFieldName, TimestampFieldName, MessageFieldName, CallerFieldName:
			continue
		}
		fields = append(fields, field)
	}
	sort.Strings(fields)

	// Write space only if something has already been written to the buffer, and if there are fields.
	if buf.Len() > 0 && len(fields) > 0 {
		buf.WriteByte(' ')
	}

	// Move the "error" field to the front
	ei := sort.Search(len(fields), func(i int) bool { return fields[i] >= ErrorFieldName })
	if ei < len(fields) && fields[ei] == ErrorFieldName {
		fields[ei] = ""
		fields = append([]string{ErrorFieldName}, fields...)
		var xfields = make([]string, 0, len(fields))
		for _, field := range fields {
			if field == "" { // Skip empty fields
				continue
			}
			xfields = append(xfields, field)
		}
		fields = xfields
	}

	for i, field := range fields {
		var fn Formatter
		var fv Formatter

		if field == ErrorFieldName {
			if w.FormatErrFieldName == nil {
				fn = consoleDefaultFormatErrFieldName(w.NoColor)
			} else {
				fn = w.FormatErrFieldName
			}

			if w.FormatErrFieldValue == nil {
				fv = consoleDefaultFormatErrFieldValue(w.NoColor)
			} else {
				fv = w.FormatErrFieldValue
			}
		} else {
			if w.FormatFieldName == nil {
				fn = consoleDefaultFormatFieldName(w.NoColor)
			} else {
				fn = w.FormatFieldName
			}

			if w.FormatFieldValue == nil {
				fv = consoleDefaultFormatFieldValue
			} else {
				fv = w.FormatFieldValue
			}
		}

		buf.WriteString(fn(field))

		switch fValue := evt[field].(type) {
		case string:
			if needsQuote(fValue) {
				buf.WriteString(fv(strconv.Quote(fValue)))
			} else {
				buf.WriteString(fv(fValue))
			}
		case json.Number:
			buf.WriteString(fv(fValue))
		default:
			b, err := InterfaceMarshalFunc(fValue)
			if err != nil {
				fmt.Fprintf(buf, colorize("[error: %v]", colorRed, w.NoColor), err)
			} else {
				fmt.Fprint(buf, fv(b))
			}
		}

		if i < len(fields)-1 { // Skip space for last field
			buf.WriteByte(' ')
		}
	}
}

// writePart appends a formatted part to buf.
func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{}, p string) {
	var f Formatter

	if w.PartsExclude != nil && len(w.PartsExclude) > 0 {
		for _, exclude := range w.PartsExclude {
			if exclude == p {
				return
			}
		}
	}

	switch p {
	case LevelFieldName:
		if w.FormatLevel == nil {
			f = consoleDefaultFormatLevel(w.NoColor)
		} else {
			f = w.FormatLevel
		}
	case TimestampFieldName:
		if w.FormatTimestamp == nil {
			f = consoleDefaultFormatTimestamp(w.TimeFormat, w.NoColor)
		} else {
			f = w.FormatTimestamp
		}
	case MessageFieldName:
		if w.FormatMessage == nil {
			f = consoleDefaultFormatMessage
		} else {
			f = w.FormatMessage
		}
	case CallerFieldName:
		if w.FormatCaller == nil {
			f = consoleDefaultFormatCaller(w.NoColor)
		} else {
			f = w.FormatCaller
		}
	default:
		if w.FormatFieldValue == nil {
			f = consoleDefaultFormatFieldValue
		} else {
			f = w.FormatFieldValue
		}
	}

	var s = f(evt[p])

	if len(s) > 0 {
		if buf.Len() > 0 {
			buf.WriteByte(' ') // Write space only if not the first part
		}
		buf.WriteString(s)
	}
}

// needsQuote returns true when the string s should be quoted in output.
func needsQuote(s string) bool {
	for i := range s {
		if s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\' || s[i] == '"' {
			return true
		}
	}
	return false
}

// colorize returns the string s wrapped in ANSI code c, unless disabled is true.
func colorize(s interface{}, c int, disabled bool) string {
	if disabled {
		return fmt.Sprintf("%s", s)
	}
	return fmt.Sprintf("\x1b[%dm%v\x1b[0m", c, s)
}

// ----- DEFAULT FORMATTERS ---------------------------------------------------

func consoleDefaultPartsOrder() []string {
	return []string{
		TimestampFieldName,
		LevelFieldName,
		CallerFieldName,
		MessageFieldName,
	}
}

func consoleDefaultFormatTimestamp(timeFormat string, noColor bool) Formatter {
	if timeFormat == "" {
		timeFormat = consoleDefaultTimeFormat
	}
	return func(i interface{}) string {
		t := "<nil>"
		switch tt := i.(type) {
		case string:
			ts, err := time.Parse(TimeFieldFormat, tt)
			if err != nil {
				t = tt
			} else {
				t = ts.Local().Format(timeFormat)
			}
		case json.Number:
			i, err := tt.Int64()
			if err != nil {
				t = tt.String()
			} else {
				var sec, nsec int64 = i, 0
				switch TimeFieldFormat {
				case TimeFormatUnixMs:
					nsec = int64(time.Duration(i) * time.Millisecond)
					sec = 0
				case TimeFormatUnixMicro:
					nsec = int64(time.Duration(i) * time.Microsecond)
					sec = 0
				}
				ts := time.Unix(sec, nsec)
				t = ts.Format(timeFormat)
			}
		}
		return colorize(t, colorDarkGray, noColor)
	}
}

func consoleDefaultFormatLevel(noColor bool) Formatter {
	return func(i interface{}) string {
		var l string
		if ll, ok := i.(string); ok {
			switch ll {
			case LevelTraceValue:
				l = colorize("TRC", colorMagenta, noColor)
			case LevelDebugValue:
				l = colorize("DBG", colorYellow, noColor)
			case LevelInfoValue:
				l = colorize("INF", colorGreen, noColor)
			case LevelWarnValue:
				l = colorize("WRN", colorRed, noColor)
			case LevelErrorValue:
				l = colorize(colorize("ERR", colorRed, noColor), colorBold, noColor)
			case LevelFatalValue:
				l = colorize(colorize("FTL", colorRed, noColor), colorBold, noColor)
			case LevelPanicValue:
				l = colorize(colorize("PNC", colorRed, noColor), colorBold, noColor)
			default:
				l = colorize("???", colorBold, noColor)
			}
		} else {
			if i == nil {
				l = colorize("???", colorBold, noColor)
			} else {
				l = strings.ToUpper(fmt.Sprintf("%s", i))[0:3]
			}
		}
		return l
	}
}

func consoleDefaultFormatCaller(noColor bool) Formatter {
	return func(i interface{}) string {
		var c string
		if cc, ok := i.(string); ok {
			c = cc
		}
		if len(c) > 0 {
			if cwd, err := os.Getwd(); err == nil {
				if rel, err := filepath.Rel(cwd, c); err == nil {
					c = rel
				}
			}
			c = colorize(c, colorBold, noColor) + colorize(" >", colorCyan, noColor)
		}
		return c
	}
}

func consoleDefaultFormatMessage(i interface{}) string {
	if i == nil {
		return ""
	}
	return fmt.Sprintf("%s", i)
}

func consoleDefaultFormatFieldName(noColor bool) Formatter {
	return func(i interface{}) string {
		return colorize(fmt.Sprintf("%s=", i), colorCyan, noColor)
	}
}

func consoleDefaultFormatFieldValue(i interface{}) string {
	return fmt.Sprintf("%s", i)
}

func consoleDefaultFormatErrFieldName(noColor bool) Formatter {
	return func(i interface{}) string {
		return colorize(fmt.Sprintf("%s=", i), colorCyan, noColor)
	}
}

func consoleDefaultFormatErrFieldValue(noColor bool) Formatter {
	return func(i interface{}) string {
		return colorize(fmt.Sprintf("%s", i), colorRed, noColor)
	}
}
<commit_msg>Show original level message if it's not predefined value. (#476)<commit_after>package zerolog

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/mattn/go-colorable"
)

const (
	colorBlack = iota + 30
	colorRed
	colorGreen
	colorYellow
	colorBlue
	colorMagenta
	colorCyan
	colorWhite

	colorBold     = 1
	colorDarkGray = 90
)

var (
	consoleBufPool = sync.Pool{
		New: func() interface{} {
			return bytes.NewBuffer(make([]byte, 0, 100))
		},
	}
)

const (
	consoleDefaultTimeFormat = time.Kitchen
)

// Formatter transforms the input into a formatted string.
type Formatter func(interface{}) string

// ConsoleWriter parses the JSON input and writes it in an
// (optionally) colorized, human-friendly format to Out.
type ConsoleWriter struct {
	// Out is the output destination.
	Out io.Writer

	// NoColor disables the colorized output.
	NoColor bool

	// TimeFormat specifies the format for timestamp in output.
	TimeFormat string

	// PartsOrder defines the order of parts in output.
	PartsOrder []string

	// PartsExclude defines parts to not display in output.
	PartsExclude []string

	// FieldsExclude defines contextual fields to not display in output.
	FieldsExclude []string

	FormatTimestamp     Formatter
	FormatLevel         Formatter
	FormatCaller        Formatter
	FormatMessage       Formatter
	FormatFieldName     Formatter
	FormatFieldValue    Formatter
	FormatErrFieldName  Formatter
	FormatErrFieldValue Formatter

	FormatExtra func(map[string]interface{}, *bytes.Buffer) error
}

// NewConsoleWriter creates and initializes a new ConsoleWriter.
func NewConsoleWriter(options ...func(w *ConsoleWriter)) ConsoleWriter {
	w := ConsoleWriter{
		Out:        os.Stdout,
		TimeFormat: consoleDefaultTimeFormat,
		PartsOrder: consoleDefaultPartsOrder(),
	}

	for _, opt := range options {
		opt(&w)
	}

	// Fix color on Windows
	if w.Out == os.Stdout || w.Out == os.Stderr {
		w.Out = colorable.NewColorable(w.Out.(*os.File))
	}

	return w
}

// Write transforms the JSON input with formatters and appends to w.Out.
func (w ConsoleWriter) Write(p []byte) (n int, err error) {
	// Fix color on Windows
	if w.Out == os.Stdout || w.Out == os.Stderr {
		w.Out = colorable.NewColorable(w.Out.(*os.File))
	}

	if w.PartsOrder == nil {
		w.PartsOrder = consoleDefaultPartsOrder()
	}

	var buf = consoleBufPool.Get().(*bytes.Buffer)
	defer func() {
		buf.Reset()
		consoleBufPool.Put(buf)
	}()

	var evt map[string]interface{}
	p = decodeIfBinaryToBytes(p)
	d := json.NewDecoder(bytes.NewReader(p))
	d.UseNumber()
	err = d.Decode(&evt)
	if err != nil {
		return n, fmt.Errorf("cannot decode event: %s", err)
	}

	for _, p := range w.PartsOrder {
		w.writePart(buf, evt, p)
	}

	w.writeFields(evt, buf)

	if w.FormatExtra != nil {
		err = w.FormatExtra(evt, buf)
		if err != nil {
			return n, err
		}
	}

	err = buf.WriteByte('\n')
	if err != nil {
		return n, err
	}

	_, err = buf.WriteTo(w.Out)
	return len(p), err
}

// writeFields appends formatted key-value pairs to buf.
func (w ConsoleWriter) writeFields(evt map[string]interface{}, buf *bytes.Buffer) {
	var fields = make([]string, 0, len(evt))
	for field := range evt {
		var isExcluded bool
		for _, excluded := range w.FieldsExclude {
			if field == excluded {
				isExcluded = true
				break
			}
		}
		if isExcluded {
			continue
		}

		switch field {
		case LevelFieldName, TimestampFieldName, MessageFieldName, CallerFieldName:
			continue
		}
		fields = append(fields, field)
	}
	sort.Strings(fields)

	// Write space only if something has already been written to the buffer, and if there are fields.
	if buf.Len() > 0 && len(fields) > 0 {
		buf.WriteByte(' ')
	}

	// Move the "error" field to the front
	ei := sort.Search(len(fields), func(i int) bool { return fields[i] >= ErrorFieldName })
	if ei < len(fields) && fields[ei] == ErrorFieldName {
		fields[ei] = ""
		fields = append([]string{ErrorFieldName}, fields...)
		var xfields = make([]string, 0, len(fields))
		for _, field := range fields {
			if field == "" { // Skip empty fields
				continue
			}
			xfields = append(xfields, field)
		}
		fields = xfields
	}

	for i, field := range fields {
		var fn Formatter
		var fv Formatter

		if field == ErrorFieldName {
			if w.FormatErrFieldName == nil {
				fn = consoleDefaultFormatErrFieldName(w.NoColor)
			} else {
				fn = w.FormatErrFieldName
			}

			if w.FormatErrFieldValue == nil {
				fv = consoleDefaultFormatErrFieldValue(w.NoColor)
			} else {
				fv = w.FormatErrFieldValue
			}
		} else {
			if w.FormatFieldName == nil {
				fn = consoleDefaultFormatFieldName(w.NoColor)
			} else {
				fn = w.FormatFieldName
			}

			if w.FormatFieldValue == nil {
				fv = consoleDefaultFormatFieldValue
			} else {
				fv = w.FormatFieldValue
			}
		}

		buf.WriteString(fn(field))

		switch fValue := evt[field].(type) {
		case string:
			if needsQuote(fValue) {
				buf.WriteString(fv(strconv.Quote(fValue)))
			} else {
				buf.WriteString(fv(fValue))
			}
		case json.Number:
			buf.WriteString(fv(fValue))
		default:
			b, err := InterfaceMarshalFunc(fValue)
			if err != nil {
				fmt.Fprintf(buf, colorize("[error: %v]", colorRed, w.NoColor), err)
			} else {
				fmt.Fprint(buf, fv(b))
			}
		}

		if i < len(fields)-1 { // Skip space for last field
			buf.WriteByte(' ')
		}
	}
}

// writePart appends a formatted part to buf.
func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{}, p string) {
	var f Formatter

	if w.PartsExclude != nil && len(w.PartsExclude) > 0 {
		for _, exclude := range w.PartsExclude {
			if exclude == p {
				return
			}
		}
	}

	switch p {
	case LevelFieldName:
		if w.FormatLevel == nil {
			f = consoleDefaultFormatLevel(w.NoColor)
		} else {
			f = w.FormatLevel
		}
	case TimestampFieldName:
		if w.FormatTimestamp == nil {
			f = consoleDefaultFormatTimestamp(w.TimeFormat, w.NoColor)
		} else {
			f = w.FormatTimestamp
		}
	case MessageFieldName:
		if w.FormatMessage == nil {
			f = consoleDefaultFormatMessage
		} else {
			f = w.FormatMessage
		}
	case CallerFieldName:
		if w.FormatCaller == nil {
			f = consoleDefaultFormatCaller(w.NoColor)
		} else {
			f = w.FormatCaller
		}
	default:
		if w.FormatFieldValue == nil {
			f = consoleDefaultFormatFieldValue
		} else {
			f = w.FormatFieldValue
		}
	}

	var s = f(evt[p])

	if len(s) > 0 {
		if buf.Len() > 0 {
			buf.WriteByte(' ') // Write space only if not the first part
		}
		buf.WriteString(s)
	}
}

// needsQuote returns true when the string s should be quoted in output.
func needsQuote(s string) bool {
	for i := range s {
		if s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\' || s[i] == '"' {
			return true
		}
	}
	return false
}

// colorize returns the string s wrapped in ANSI code c, unless disabled is true.
func colorize(s interface{}, c int, disabled bool) string {
	if disabled {
		return fmt.Sprintf("%s", s)
	}
	return fmt.Sprintf("\x1b[%dm%v\x1b[0m", c, s)
}

// ----- DEFAULT FORMATTERS ---------------------------------------------------

func consoleDefaultPartsOrder() []string {
	return []string{
		TimestampFieldName,
		LevelFieldName,
		CallerFieldName,
		MessageFieldName,
	}
}

func consoleDefaultFormatTimestamp(timeFormat string, noColor bool) Formatter {
	if timeFormat == "" {
		timeFormat = consoleDefaultTimeFormat
	}
	return func(i interface{}) string {
		t := "<nil>"
		switch tt := i.(type) {
		case string:
			ts, err := time.Parse(TimeFieldFormat, tt)
			if err != nil {
				t = tt
			} else {
				t = ts.Local().Format(timeFormat)
			}
		case json.Number:
			i, err := tt.Int64()
			if err != nil {
				t = tt.String()
			} else {
				var sec, nsec int64 = i, 0
				switch TimeFieldFormat {
				case TimeFormatUnixMs:
					nsec = int64(time.Duration(i) * time.Millisecond)
					sec = 0
				case TimeFormatUnixMicro:
					nsec = int64(time.Duration(i) * time.Microsecond)
					sec = 0
				}
				ts := time.Unix(sec, nsec)
				t = ts.Format(timeFormat)
			}
		}
		return colorize(t, colorDarkGray, noColor)
	}
}

func consoleDefaultFormatLevel(noColor bool) Formatter {
	return func(i interface{}) string {
		var l string
		if ll, ok := i.(string); ok {
			switch ll {
			case LevelTraceValue:
				l = colorize("TRC", colorMagenta, noColor)
			case LevelDebugValue:
				l = colorize("DBG", colorYellow, noColor)
			case LevelInfoValue:
				l = colorize("INF", colorGreen, noColor)
			case LevelWarnValue:
				l = colorize("WRN", colorRed, noColor)
			case LevelErrorValue:
				l = colorize(colorize("ERR", colorRed, noColor), colorBold, noColor)
			case LevelFatalValue:
				l = colorize(colorize("FTL", colorRed, noColor), colorBold, noColor)
			case LevelPanicValue:
				l = colorize(colorize("PNC", colorRed, noColor), colorBold, noColor)
			default:
				l = colorize(ll, colorBold, noColor)
			}
		} else {
			if i == nil {
				l = colorize("???", colorBold, noColor)
			} else {
				l = strings.ToUpper(fmt.Sprintf("%s", i))[0:3]
			}
		}
		return l
	}
}

func consoleDefaultFormatCaller(noColor bool) Formatter {
	return func(i interface{}) string {
		var c string
		if cc, ok := i.(string); ok {
			c = cc
		}
		if len(c) > 0 {
			if cwd, err := os.Getwd(); err == nil {
				if rel, err := filepath.Rel(cwd, c); err == nil {
					c = rel
				}
			}
			c = colorize(c, colorBold, noColor) + colorize(" >", colorCyan, noColor)
		}
		return c
	}
}

func consoleDefaultFormatMessage(i interface{}) string {
	if i == nil {
		return ""
	}
	return fmt.Sprintf("%s", i)
}

func consoleDefaultFormatFieldName(noColor bool) Formatter {
	return func(i interface{}) string {
		return colorize(fmt.Sprintf("%s=", i), colorCyan, noColor)
	}
}

func consoleDefaultFormatFieldValue(i interface{}) string {
	return fmt.Sprintf("%s", i)
}

func consoleDefaultFormatErrFieldName(noColor bool) Formatter {
	return func(i interface{}) string {
		return colorize(fmt.Sprintf("%s=", i), colorCyan, noColor)
	}
}

func consoleDefaultFormatErrFieldValue(noColor bool) Formatter {
	return func(i interface{}) string {
		return colorize(fmt.Sprintf("%s", i), colorRed, noColor)
	}
}
<|endoftext|>


<commit_before>package output

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"text/template"
	"unicode/utf8"
)

type Output struct {
	Writer io.Writer

	rows     [][]string
	lengths  map[int]int
	hasNames bool
}

func (o *Output) Add(row ...string) {
	o.rows = append(o.rows, row)
	o.updateLengths(row)
}

func (o *Output) SetNames(names ...string) {
	o.updateLengths(names)
	if o.hasNames {
		o.rows[0] = names
	} else {
		o.rows = append([][]string{names}, o.rows...)
		o.hasNames = true
	}
}

func (o *Output) updateLengths(row []string) {
	if len(o.lengths) == 0 {
		o.lengths = make(map[int]int)
	}
	for i, col := range row {
		length := utf8.RuneCountInString(col)
		if o.lengths[i] < length {
			o.lengths[i] = length
		}
	}
}

func (o *Output) Table(spaces int) {
	if o.Writer == nil {
		o.Writer = os.Stdout
	}
	for i, row := range o.rows {
		for j, col := range row {
			n := strconv.Itoa(o.lengths[j] + spaces)
			if o.hasNames && i == 0 {
				col = strings.ToUpper(col)
			}
			fmt.Fprintf(o.Writer, "%-"+n+"s", col)
		}
		fmt.Fprintln(o.Writer, "")
	}
}

func (o *Output) CSV() error {
	if o.Writer == nil {
		o.Writer = os.Stdout
	}
	w := csv.NewWriter(o.Writer)
	if o.hasNames {
		return w.WriteAll(o.rows[1:])
	}
	return w.WriteAll(o.rows)
}

func (o *Output) Template(s string) error {
	if o.Writer == nil {
		o.Writer = os.Stdout
	}
	t := template.Must(template.New("output").Parse(s))
	if o.hasNames {
		return o.namedDataTemplate(t)
	}
	return t.Execute(o.Writer, o.rows)
}

func (o *Output) namedDataTemplate(t *template.Template) error {
	names := o.rows[0]
	for i, name := range names {
		name := strings.ToLower(name)
		name = strings.Title(name)
		names[i] = strings.Replace(name, " ", "", -1)
	}
	data := []map[string]string{}
	for _, row := range o.rows[1:] {
		m := map[string]string{}
		for i, col := range row {
			m[names[i]] = col
		}
		data = append(data, m)
	}
	return t.Execute(o.Writer, data)
}
<commit_msg>Ability to execute template with single map, not a slice<commit_after>package output

import (
	"encoding/csv"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"text/template"
	"unicode/utf8"
)

type Output struct {
	Writer   io.Writer
	IsSingle bool

	rows     [][]string
	lengths  map[int]int
	hasNames bool
}

func (o *Output) Add(row ...string) {
	o.rows = append(o.rows, row)
	o.updateLengths(row)
}

func (o *Output) SetNames(names ...string) {
	o.updateLengths(names)
	if o.hasNames {
		o.rows[0] = names
	} else {
		o.rows = append([][]string{names}, o.rows...)
		o.hasNames = true
	}
}

func (o *Output) updateLengths(row []string) {
	if len(o.lengths) == 0 {
		o.lengths = make(map[int]int)
	}
	for i, col := range row {
		length := utf8.RuneCountInString(col)
		if o.lengths[i] < length {
			o.lengths[i] = length
		}
	}
}

func (o *Output) Table(spaces int) {
	if o.Writer == nil {
		o.Writer = os.Stdout
	}
	for i, row := range o.rows {
		for j, col := range row {
			n := strconv.Itoa(o.lengths[j] + spaces)
			if o.hasNames && i == 0 {
				col = strings.ToUpper(col)
			}
			fmt.Fprintf(o.Writer, "%-"+n+"s", col)
		}
		fmt.Fprintln(o.Writer, "")
	}
}

func (o *Output) CSV() error {
	if o.Writer == nil {
		o.Writer = os.Stdout
	}
	w := csv.NewWriter(o.Writer)
	if o.hasNames {
		return w.WriteAll(o.rows[1:])
	}
	return w.WriteAll(o.rows)
}

func (o *Output) Template(s string) error {
	if o.Writer == nil {
		o.Writer = os.Stdout
	}
	s = o.sanitizeTemplate(s)
	t := template.Must(template.New("output").Parse(s))
	if o.hasNames {
		return o.namedDataTemplate(t)
	}
	if o.IsSingle {
		return t.Execute(o.Writer, o.rows[0])
	}
	return t.Execute(o.Writer, o.rows)
}

func (o *Output) sanitizeTemplate(s string) string {
	if !strings.HasSuffix(strings.TrimSpace(s), "\n") {
		s = s + "\n"
	}
	return s
}

func (o *Output) namedDataTemplate(t *template.Template) error {
	names := o.rows[0]
	for i, name := range names {
		name := strings.ToLower(name)
		name = strings.Title(name)
		names[i] = strings.Replace(name, " ", "", -1)
	}
	data := []map[string]string{}
	for _, row := range o.rows[1:] {
		m := map[string]string{}
		for i, col := range row {
			m[names[i]] = col
		}
		data = append(data, m)
	}
	if o.IsSingle {
		return t.Execute(o.Writer, data[0])
	}
	return t.Execute(o.Writer, data)
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package output 对解析后的数据进行渲染输出。
package output

import (
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/caixw/apidoc/app"
	"github.com/caixw/apidoc/doc"
	"github.com/caixw/apidoc/locale"

	"github.com/issue9/utils"
)

// Options 指定了渲染输出的相关设置项。
type Options struct {
	Dir      string        `yaml:"dir"` // 文档的保存目录
	Elapsed  time.Duration `yaml:"-"`   // 编译用时
	ErrorLog *log.Logger   `yaml:"-"`   // 错误信息输出通道，在 html+ 模式下会用到。

	dataDir string // json 数据保存的目录
}

// Init 对 Options 作一些初始化操作。
func (o *Options) Init() *app.OptionsError {
	if len(o.Dir) == 0 {
		return &app.OptionsError{Field: "dir", Message: locale.Sprintf(locale.ErrRequired)}
	}

	if !utils.FileExists(o.Dir) {
		if err := os.MkdirAll(o.Dir, os.ModePerm); err != nil {
			msg := locale.Sprintf(locale.ErrMkdirError, err)
			return &app.OptionsError{Field: "dir", Message: msg}
		}
	}

	o.dataDir = filepath.Join(o.Dir, app.JSONDataDirName)
	if !utils.FileExists(o.dataDir) {
		if err := os.MkdirAll(o.dataDir, os.ModePerm); err != nil {
			msg := locale.Sprintf(locale.ErrMkdirError, err)
			return &app.OptionsError{Field: "dir", Message: msg}
		}
	}

	return nil
}

// Render 渲染 docs 的内容，具体的渲染参数由 o 指定。
func Render(docs *doc.Doc, o *Options) error {
	return render(docs, o)
}
<commit_msg>将 static 输出添加到 output 中<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package output 对解析后的数据进行渲染输出。
package output

import (
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/caixw/apidoc/app"
	"github.com/caixw/apidoc/doc"
	"github.com/caixw/apidoc/locale"
	"github.com/caixw/apidoc/output/static"

	"github.com/issue9/utils"
)

// Options 指定了渲染输出的相关设置项。
type Options struct {
	Dir      string        `yaml:"dir"` // 文档的保存目录
	Elapsed  time.Duration `yaml:"-"`   // 编译用时
	ErrorLog *log.Logger   `yaml:"-"`   // 错误信息输出通道，在 html+ 模式下会用到。

	dataDir string // json 数据保存的目录
}

// Init 对 Options 作一些初始化操作。
func (o *Options) Init() *app.OptionsError {
	if len(o.Dir) == 0 {
		return &app.OptionsError{Field: "dir", Message: locale.Sprintf(locale.ErrRequired)}
	}

	if !utils.FileExists(o.Dir) {
		if err := os.MkdirAll(o.Dir, os.ModePerm); err != nil {
			msg := locale.Sprintf(locale.ErrMkdirError, err)
			return &app.OptionsError{Field: "dir", Message: msg}
		}
	}

	o.dataDir = filepath.Join(o.Dir, app.JSONDataDirName)
	if !utils.FileExists(o.dataDir) {
		if err := os.MkdirAll(o.dataDir, os.ModePerm); err != nil {
			msg := locale.Sprintf(locale.ErrMkdirError, err)
			return &app.OptionsError{Field: "dir", Message: msg}
		}
	}

	return nil
}

// Render 渲染 docs 的内容，具体的渲染参数由 o 指定。
func Render(docs *doc.Doc, o *Options) error {
	if err := static.Output(o.Dir); err != nil {
		return err
	}

	return render(docs, o)
}
<|endoftext|>


<commit_before>// Package parser defines an interface for parsers (creating templates) and templates (rendering content), and defines a base template type which conforms to both interfaces and can be included in any templates
package parser

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

// Scanner scans paths for templates and creates a representation of each using parsers
type Scanner struct {
	// A map of all templates keyed by path name
	Templates map[string]Template

	// A set of parsers (in order) with which to parse templates
	Parsers []Parser

	// A set of paths (in order) from which to load templates
	Paths []string

	// Helpers is a list of helper functions
	Helpers FuncMap

	// rootPath is used to store the root path during scans
	rootPath string
}

// NewScanner creates a new template scanner
func NewScanner(paths []string, helpers FuncMap) (*Scanner, error) {
	s := &Scanner{
		Helpers:   helpers,
		Paths:     paths,
		Templates: make(map[string]Template),
		Parsers:   []Parser{new(JSONTemplate), new(HTMLTemplate), new(TextTemplate)},
	}

	return s, nil
}

// ScanPath scans a path for template files, including sub-paths
func (s *Scanner) ScanPath(root string) error {

	// Store the rootPath - used in walkFunc
	s.rootPath = path.Clean(root)

	// Store current path, and change to root path
	// so that template includes use relative paths from root
	// this may not be necc. any more, test removing it
	pwd, err := os.Getwd()
	if err != nil {
		return err
	}

	// Change dir to the rootPath so that paths are relative
	err = os.Chdir(s.rootPath)
	if err != nil {
		return err
	}

	err = filepath.Walk(".", s.walkFunc)
	if err != nil {
		return err
	}

	// Change back to original path
	err = os.Chdir(pwd)
	if err != nil {
		return err
	}

	return nil
}

// walkFunc handles files from filepath.Walk in ScanPath
// It follows symlinks where encountered by recursing
func (s *Scanner) walkFunc(path string, info os.FileInfo, err error) error {

	if strings.Contains(path, "guides") {
		fmt.Printf("WalkFunc:%s\n", path)
	}

	// If an error occurred, report it
	if err != nil {
		return err
	}

	// Check if this is a symlink, if so recurse
	// This assumes that the structure at linkedPath exactly mirrors that at path
	if isSymlink(info) {

		// Find the linked path
		linkedPath, err := filepath.EvalSymlinks(path)
		if err != nil {
			return fmt.Errorf("error reading symbolic link: %s", err)
		}

		// Calculate a new temp root path, based on linked path
		// trimmed of the original path
		// This assumes that the structure at linkedPath exactly mirrors that at path
		newRoot := strings.TrimSuffix(linkedPath, path)

		// Store current dir
		pwd, err := os.Getwd()
		if err != nil {
			return err
		}

		// Change dir to the linked path container
		err = os.Chdir(newRoot)
		if err != nil {
			return err
		}

		// filepath.Walk at the location, based on the newRoot
		err = filepath.Walk(path, s.walkFunc)

		// Change dir back to pwd
		err = os.Chdir(pwd)
		if err != nil {
			return err
		}

		return err
	}

	// Deal with files, directories we return nil error to recurse on them
	if !info.IsDir() {
		// Ask parsers in turn to handle the file - first one to claim it wins
		for _, p := range s.Parsers {
			if p.CanParseFile(path) {

				fullpath := filepath.Join(s.rootPath, path)
				t, err := p.NewTemplate(fullpath, path)
				if err != nil {
					return err
				}

				s.Templates[path] = t
				return nil
			}
		}

	}

	return nil
}

// isSymlink returns true if this is a symlink
func isSymlink(info os.FileInfo) bool {
	return info.Mode()&os.ModeSymlink != 0
}

// ScanPaths resets template list and rescans all template paths
func (s *Scanner) ScanPaths() error {
	// Make sure templates is empty
	s.Templates = make(map[string]Template)

	// Set up the parsers
	for _, p := range s.Parsers {
		err := p.Setup(s.Helpers)
		if err != nil {
			return err
		}
	}

	// Scan paths again
	for _, p := range s.Paths {
		err := s.ScanPath(p)
		if err != nil {
			return err
		}
	}

	// Now parse and finalize templates
	for _, t := range s.Templates {
		err := t.Parse()
		if err != nil {
			return err
		}
	}

	// Now finalize templates
	for _, t := range s.Templates {
		err := t.Finalize(s.Templates)
		if err != nil {
			return err
		}
	}

	return nil
}

// PATH UTILITIES

// dotFile returns true if the file path supplied a dot file?
func dotFile(p string) bool {
	return strings.HasPrefix(path.Base(p), ".")
}

// suffix returns true if the path have this suffix (ignoring dotfiles)?
func suffix(p string, suffix string) bool {
	if dotFile(p) {
		return false
	}
	return strings.HasSuffix(p, suffix)
}

// suffixes returns true if the path has these suffixes (ignoring dotfiles)?
func suffixes(p string, suffixes []string) bool {
	if dotFile(p) {
		return false
	}

	for _, s := range suffixes {
		if strings.HasSuffix(p, s) {
			return true
		}
	}

	return false
}
<commit_msg>Remove spurious logging line<commit_after>// Package parser defines an interface for parsers (creating templates) and templates (rendering content), and defines a base template type which conforms to both interfaces and can be included in any templates
package parser

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

// Scanner scans paths for templates and creates a representation of each using parsers
type Scanner struct {
	// A map of all templates keyed by path name
	Templates map[string]Template

	// A set of parsers (in order) with which to parse templates
	Parsers []Parser

	// A set of paths (in order) from which to load templates
	Paths []string

	// Helpers is a list of helper functions
	Helpers FuncMap

	// rootPath is used to store the root path during scans
	rootPath string
}

// NewScanner creates a new template scanner
func NewScanner(paths []string, helpers FuncMap) (*Scanner, error) {
	s := &Scanner{
		Helpers:   helpers,
		Paths:     paths,
		Templates: make(map[string]Template),
		Parsers:   []Parser{new(JSONTemplate), new(HTMLTemplate), new(TextTemplate)},
	}

	return s, nil
}

// ScanPath scans a path for template files, including sub-paths
func (s *Scanner) ScanPath(root string) error {

	// Store the rootPath - used in walkFunc
	s.rootPath = path.Clean(root)

	// Store current path, and change to root path
	// so that template includes use relative paths from root
	// this may not be necc. any more, test removing it
	pwd, err := os.Getwd()
	if err != nil {
		return err
	}

	// Change dir to the rootPath so that paths are relative
	err = os.Chdir(s.rootPath)
	if err != nil {
		return err
	}

	err = filepath.Walk(".", s.walkFunc)
	if err != nil {
		return err
	}

	// Change back to original path
	err = os.Chdir(pwd)
	if err != nil {
		return err
	}

	return nil
}

// walkFunc handles files from filepath.Walk in ScanPath
// It follows symlinks where encountered by recursing
func (s *Scanner) walkFunc(path string, info os.FileInfo, err error) error {

	// If an error occurred, report it
	if err != nil {
		return err
	}

	// Check if this is a symlink, if so recurse
	// This assumes that the structure at linkedPath exactly mirrors that at path
	if isSymlink(info) {

		// Find the linked path
		linkedPath, err := filepath.EvalSymlinks(path)
		if err != nil {
			return fmt.Errorf("error reading symbolic link: %s", err)
		}

		// Calculate a new temp root path, based on linked path
		// trimmed of the original path
		// This assumes that the structure at linkedPath exactly mirrors that at path
		newRoot := strings.TrimSuffix(linkedPath, path)

		// Store current dir
		pwd, err := os.Getwd()
		if err != nil {
			return err
		}

		// Change dir to the linked path container
		err = os.Chdir(newRoot)
		if err != nil {
			return err
		}

		// filepath.Walk at the location, based on the newRoot
		err = filepath.Walk(path, s.walkFunc)

		// Change dir back to pwd
		err = os.Chdir(pwd)
		if err != nil {
			return err
		}

		return err
	}

	// Deal with files, directories we return nil error to recurse on them
	if !info.IsDir() {
		// Ask parsers in turn to handle the file - first one to claim it wins
		for _, p := range s.Parsers {
			if p.CanParseFile(path) {

				fullpath := filepath.Join(s.rootPath, path)
				t, err := p.NewTemplate(fullpath, path)
				if err != nil {
					return err
				}

				s.Templates[path] = t
				return nil
			}
		}

	}

	return nil
}

// isSymlink returns true if this is a symlink
func isSymlink(info os.FileInfo) bool {
	return info.Mode()&os.ModeSymlink != 0
}

// ScanPaths resets template list and rescans all template paths
func (s *Scanner) ScanPaths() error {
	// Make sure templates is empty
	s.Templates = make(map[string]Template)

	// Set up the parsers
	for _, p := range s.Parsers {
		err := p.Setup(s.Helpers)
		if err != nil {
			return err
		}
	}

	// Scan paths again
	for _, p := range s.Paths {
		err := s.ScanPath(p)
		if err != nil {
			return err
		}
	}

	// Now parse and finalize templates
	for _, t := range s.Templates {
		err := t.Parse()
		if err != nil {
			return err
		}
	}

	// Now finalize templates
	for _, t := range s.Templates {
		err := t.Finalize(s.Templates)
		if err != nil {
			return err
		}
	}

	return nil
}

// PATH UTILITIES

// dotFile returns true if the file path supplied a dot file?
func dotFile(p string) bool {
	return strings.HasPrefix(path.Base(p), ".")
}

// suffix returns true if the path have this suffix (ignoring dotfiles)?
func suffix(p string, suffix string) bool {
	if dotFile(p) {
		return false
	}
	return strings.HasSuffix(p, suffix)
}

// suffixes returns true if the path has these suffixes (ignoring dotfiles)?
func suffixes(p string, suffixes []string) bool {
	if dotFile(p) {
		return false
	}

	for _, s := range suffixes {
		if strings.HasSuffix(p, s) {
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package config

import (
	"xd/lib/bittorrent/swarm"
	"xd/lib/configparser"
	"xd/lib/storage"
	"xd/lib/util"
)

const DefaultOpentrackerFilename = "trackers.ini"

// TODO: idk if these are the right names but the URL are correct
var DefaultOpenTrackers = map[string]string{} /*
	"dg-opentracker":       "http://w7tpbzncbcocrqtwwm3nezhnnsw4ozadvi2hmvzdhrqzfxfum7wa.b32.i2p/a",
	"thebland-opentracker": "http://s5ikrdyjwbcgxmqetxb3nyheizftms7euacuub2hic7defkh3xhq.b32.i2p/a",
	"psi-chihaya":          "http://uajd4nctepxpac4c4bdyrdw7qvja2a5u3x25otfhkptcjgd53ioq.b32.i2p/announce",
}
*/

type TrackerConfig struct {
	Trackers map[string]string
	FileName string
}

func (c *TrackerConfig) Save() (err error) {
	if c.Trackers == nil || len(c.Trackers) == 0 {
		c.Trackers = DefaultOpenTrackers
	}
	cfg := configparser.NewConfiguration()
	for sect := range c.Trackers {
		s := cfg.NewSection(sect)
		s.Add("url", c.Trackers[sect])
	}
	err = configparser.Save(cfg, c.FileName)
	return
}

func (c *TrackerConfig) Load() (err error) {

	if len(c.FileName) == 0 {
		c.FileName = DefaultOpentrackerFilename
	}

	// create defaults
	if !util.CheckFile(c.FileName) {
		err = c.Save()
	}

	if err == nil {
		var cfg *configparser.Configuration
		cfg, err = configparser.Read(c.FileName)
		if err == nil {
			var sects []*configparser.Section
			sects, err = cfg.AllSections()
			if err == nil {
				c.Trackers = make(map[string]string)
				for idx := range sects {
					if sects[idx].Exists("url") {
						c.Trackers[sects[idx].Name()] = sects[idx].ValueOf("url")
					}
				}
			}
		}
	}
	return
}

type BittorrentConfig struct {
	DHT          bool
	PEX          bool
	OpenTrackers TrackerConfig
}

func (c *BittorrentConfig) Load(s *configparser.Section) error {
	c.OpenTrackers.FileName = DefaultOpentrackerFilename
	if s != nil {
		c.DHT = s.Get("dht", "0") == "1"
		c.PEX = s.Get("pex", "0") == "1"
		c.OpenTrackers.FileName = s.Get("tracker-config", c.OpenTrackers.FileName)
	}
	return c.OpenTrackers.Load()
}

func (c *BittorrentConfig) Save(s *configparser.Section) error {
	if c.PEX {
		s.Add("pex", "1")
	} else {
		s.Add("pex", "0")
	}

	if c.DHT {
		s.Add("dht", "1")
	} else {
		s.Add("dht", "0")
	}

	s.Add("tracker-config", c.OpenTrackers.FileName)

	return c.OpenTrackers.Save()
}

func (cfg *BittorrentConfig) LoadEnv() {

}

func (c *BittorrentConfig) CreateSwarm(st storage.Storage) *swarm.Swarm {
	sw := swarm.NewSwarm(st)
	for name := range c.OpenTrackers.Trackers {
		sw.AddOpenTracker(c.OpenTrackers.Trackers[name])
	}
	return sw
}
<commit_msg>add environmental variable for overriding open tracker<commit_after>package config

import (
	"os"
	"xd/lib/bittorrent/swarm"
	"xd/lib/configparser"
	"xd/lib/storage"
	"xd/lib/util"
)

const DefaultOpentrackerFilename = "trackers.ini"

// TODO: idk if these are the right names but the URL are correct
var DefaultOpenTrackers = map[string]string{} /*
	"dg-opentracker":       "http://w7tpbzncbcocrqtwwm3nezhnnsw4ozadvi2hmvzdhrqzfxfum7wa.b32.i2p/a",
	"thebland-opentracker": "http://s5ikrdyjwbcgxmqetxb3nyheizftms7euacuub2hic7defkh3xhq.b32.i2p/a",
	"psi-chihaya":          "http://uajd4nctepxpac4c4bdyrdw7qvja2a5u3x25otfhkptcjgd53ioq.b32.i2p/announce",
}
*/

type TrackerConfig struct {
	Trackers map[string]string
	FileName string
}

func (c *TrackerConfig) Save() (err error) {
	if c.Trackers == nil || len(c.Trackers) == 0 {
		c.Trackers = DefaultOpenTrackers
	}
	cfg := configparser.NewConfiguration()
	for sect := range c.Trackers {
		s := cfg.NewSection(sect)
		s.Add("url", c.Trackers[sect])
	}
	err = configparser.Save(cfg, c.FileName)
	return
}

func (c *TrackerConfig) Load() (err error) {

	if len(c.FileName) == 0 {
		c.FileName = DefaultOpentrackerFilename
	}

	// create defaults
	if !util.CheckFile(c.FileName) {
		err = c.Save()
	}

	if err == nil {
		var cfg *configparser.Configuration
		cfg, err = configparser.Read(c.FileName)
		if err == nil {
			var sects []*configparser.Section
			sects, err = cfg.AllSections()
			if err == nil {
				c.Trackers = make(map[string]string)
				for idx := range sects {
					if sects[idx].Exists("url") {
						c.Trackers[sects[idx].Name()] = sects[idx].ValueOf("url")
					}
				}
			}
		}
	}
	return
}

type BittorrentConfig struct {
	DHT          bool
	PEX          bool
	OpenTrackers TrackerConfig
}

func (c *BittorrentConfig) Load(s *configparser.Section) error {
	c.OpenTrackers.FileName = DefaultOpentrackerFilename
	if s != nil {
		c.DHT = s.Get("dht", "0") == "1"
		c.PEX = s.Get("pex", "0") == "1"
		c.OpenTrackers.FileName = s.Get("tracker-config", c.OpenTrackers.FileName)
	}
	return c.OpenTrackers.Load()
}

func (c *BittorrentConfig) Save(s *configparser.Section) error {
	if c.PEX {
		s.Add("pex", "1")
	} else {
		s.Add("pex", "0")
	}

	if c.DHT {
		s.Add("dht", "1")
	} else {
		s.Add("dht", "0")
	}

	s.Add("tracker-config", c.OpenTrackers.FileName)

	return c.OpenTrackers.Save()
}

const EnvOpenTracker = "XD_OPENTRACKER_URL"

func (cfg *BittorrentConfig) LoadEnv() {
	url := os.Getenv(EnvOpenTracker)
	if url != "" {
		cfg.OpenTrackers.Trackers = map[string]string{
			"default": url,
		}
	}
}

func (c *BittorrentConfig) CreateSwarm(st storage.Storage) *swarm.Swarm {
	sw := swarm.NewSwarm(st)
	for name := range c.OpenTrackers.Trackers {
		sw.AddOpenTracker(c.OpenTrackers.Trackers[name])
	}
	return sw
}
<|endoftext|>


<commit_before>// Package sftz is a timezone utility package for Go Snowflake Driver
//
// Copyright (c) 2017 Snowflake Computing Inc. All right reserved.
//
package gosnowflake

import (
	"errors"
	"testing"
)

type tcLocation struct {
	ss  string
	tt  string
	err error
}

func TestWithOffsetString(t *testing.T) {
	testcases := []tcLocation{
		{
			ss:  "+0700",
			tt:  "+0700",
			err: nil,
		},
		{
			ss:  "-1200",
			tt:  "-1200",
			err: nil,
		},
		{
			ss:  "+0710",
			tt:  "+0710",
			err: nil,
		},
		{
			ss: "1200",
			tt: "-1200",
			err: &SnowflakeError{
				Number:      ErrInvalidOffsetStr,
				Message:     errMsgInvalidOffsetStr,
				MessageArgs: []interface{}{"1200"},
			},
		},
		{
			ss: "+12001",
			tt: "",
			err: &SnowflakeError{
				Number:      ErrInvalidOffsetStr,
				Message:     errMsgInvalidOffsetStr,
				MessageArgs: []interface{}{"+12001"},
			},
		},
		{
			ss: "x12001",
			tt: "",
			err: &SnowflakeError{
				Number:      ErrInvalidOffsetStr,
				Message:     errMsgInvalidOffsetStr,
				MessageArgs: []interface{}{"x12001"},
			},
		},
		{
			ss:  "-12CD",
			tt:  "",
			err: errors.New("parse int error"), // can this be more specific?
		},
		{
			ss:  "+ABCD",
			tt:  "",
			err: errors.New("parse int error"), // can this be more specific?
		},
	}
	for _, t0 := range testcases {
		loc, err := LocationWithOffsetString(t0.ss)
		if t0.err != nil {
			if t0.err != err {
				driverError1, ok1 := t0.err.(*SnowflakeError)
				driverError2, ok2 := err.(*SnowflakeError)
				if ok1 && ok2 && driverError1.Number != driverError2.Number {
					t.Fatalf("error expected: %v, got: %v", t0.err, err)
				}
			}
		} else {
			if err != nil {
				t.Fatalf("%v", err)
			}
			if t0.tt != loc.String() {
				t.Fatalf("location string didn't match. expected: %v, got: %v", t0.tt, loc)
			}
		}
	}
}
<commit_msg>added more location test<commit_after>// Package sftz is a timezone utility package for Go Snowflake Driver
//
// Copyright (c) 2017 Snowflake Computing Inc. All right reserved.
//
package gosnowflake

import (
	"errors"
	"testing"
)

type tcLocation struct {
	ss  string
	tt  string
	err error
}

func TestWithOffsetString(t *testing.T) {
	testcases := []tcLocation{
		{
			ss:  "+0700",
			tt:  "+0700",
			err: nil,
		},
		{
			ss:  "-1200",
			tt:  "-1200",
			err: nil,
		},
		{
			ss:  "+0710",
			tt:  "+0710",
			err: nil,
		},
		{
			ss: "1200",
			tt: "",
			err: &SnowflakeError{
				Number:      ErrInvalidOffsetStr,
				Message:     errMsgInvalidOffsetStr,
				MessageArgs: []interface{}{"1200"},
			},
		},
		{
			ss: "x1200",
			tt: "",
			err: &SnowflakeError{
				Number:      ErrInvalidOffsetStr,
				Message:     errMsgInvalidOffsetStr,
				MessageArgs: []interface{}{"x1200"},
			},
		},
		{
			ss: "+12001",
			tt: "",
			err: &SnowflakeError{
				Number:      ErrInvalidOffsetStr,
				Message:     errMsgInvalidOffsetStr,
				MessageArgs: []interface{}{"+12001"},
			},
		},
		{
			ss: "x12001",
			tt: "",
			err: &SnowflakeError{
				Number:      ErrInvalidOffsetStr,
				Message:     errMsgInvalidOffsetStr,
				MessageArgs: []interface{}{"x12001"},
			},
		},
		{
			ss:  "-12CD",
			tt:  "",
			err: errors.New("parse int error"), // can this be more specific?
		},
		{
			ss:  "+ABCD",
			tt:  "",
			err: errors.New("parse int error"), // can this be more specific?
		},
	}
	for _, t0 := range testcases {
		loc, err := LocationWithOffsetString(t0.ss)
		if t0.err != nil {
			if t0.err != err {
				driverError1, ok1 := t0.err.(*SnowflakeError)
				driverError2, ok2 := err.(*SnowflakeError)
				if ok1 && ok2 && driverError1.Number != driverError2.Number {
					t.Fatalf("error expected: %v, got: %v", t0.err, err)
				}
			}
		} else {
			if err != nil {
				t.Fatalf("%v", err)
			}
			if t0.tt != loc.String() {
				t.Fatalf("location string didn't match. expected: %v, got: %v", t0.tt, loc)
			}
		}
	}
}
<|endoftext|>


<commit_before>package loggly

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gliderlabs/logspout/router"
)

const (
	adapterName         = "loggly"
	logglyTokenEnvVar   = "LOGGLY_TOKEN"
	logglyTagsEnvVar		= "LOGGLY_TAGS"
	logglyAddr          = "https://logs-01.loggly.com"
	logglyEventEndpoint = "/inputs"
)

// TODO: consider logging all fatals to loggly

func init() {
	router.AdapterFactories.Register(NewLogglyAdapter, adapterName)

	r := &router.Route{
		Adapter: "loggly",
	}

	// It's not documented in the logspout repo but if you want to use an adapter without
	// going through the routesapi you must add at #init or via #New...
	err := router.Routes.Add(r)
	if err != nil {
		log.Fatal("Could not add route: ", err.Error())
	}
}

// NewLogglyAdapter returns an Adapter with that uses a loggly token taken from
// the LOGGLY_TOKEN environment variable
func NewLogglyAdapter(route *router.Route) (router.LogAdapter, error) {
	token := os.Getenv(logglyTokenEnvVar)

	if token == "" {
		return nil, errors.New("Could not find environment variable LOGGLY_TOKEN")
	}

	return &Adapter{
		token:  token,
		client: http.Client{},
	}, nil
}

// Adapter satisfies the router.LogAdapter interface by providing Stream which
// passes all messages to loggly.
type Adapter struct {
	token  string
	client http.Client
}

// Stream satisfies the router.LogAdapter interface and passes all messages to Loggly
func (l *Adapter) Stream(logstream chan *router.Message) {
	for m := range logstream {
		msg := logglyMessage{
			Message:           m.Data,
			ContainerName:     m.Container.Name,
			ContainerID:       m.Container.ID,
			ContainerImage:    m.Container.Config.Image,
			ContainerHostname: m.Container.Config.Hostname,
		}

		err := l.SendMessage(msg)

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

// SendMessage handles creating and sending a request to Loggly. Any errors that occur during that
// process are bubbled up to the caller
func (l *Adapter) SendMessage(msg logglyMessage) error {
	js, err := json.Marshal(msg)

	if err != nil {
		return err
	}

	url := fmt.Sprintf("%s%s/%s", logglyAddr, logglyEventEndpoint, l.token, "tag", os.Getenv(logglyTagsEnvVar))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(js))

	req.Header.Add("X-LOGGLY-TAG", os.Getenv(logglyTagsEnvVar))

	if err != nil {
		return err
	}

	// TODO: possibly use pool of workers to send requests?
	resp, err := l.client.Do(req)

	if err != nil {
		errMsg := fmt.Sprintf("Error from client: %s", err.Error())
		return errors.New(errMsg)
	}

	if resp.StatusCode != http.StatusOK {
		errMsg := fmt.Sprintf("Received a non 200 status code: %s", err.Error())
		return errors.New(errMsg)
	}

	return nil
}

type logglyMessage struct {
	Message           string `json:"message"`
	ContainerName     string `json:"container_name"`
	ContainerID       string `json:"container_id"`
	ContainerImage    string `json:"container_image"`
	ContainerHostname string `json:"hostname"`
}
<commit_msg>Fixing formatting<commit_after>package loggly

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/gliderlabs/logspout/router"
)

const (
	adapterName         = "loggly"
	logglyTokenEnvVar   = "LOGGLY_TOKEN"
	logglyTagsEnvVar    = "LOGGLY_TAGS"
	logglyAddr          = "https://logs-01.loggly.com"
	logglyEventEndpoint = "/inputs"
)

// TODO: consider logging all fatals to loggly

func init() {
	router.AdapterFactories.Register(NewLogglyAdapter, adapterName)

	r := &router.Route{
		Adapter: "loggly",
	}

	// It's not documented in the logspout repo but if you want to use an adapter without
	// going through the routesapi you must add at #init or via #New...
	err := router.Routes.Add(r)
	if err != nil {
		log.Fatal("Could not add route: ", err.Error())
	}
}

// NewLogglyAdapter returns an Adapter with that uses a loggly token taken from
// the LOGGLY_TOKEN environment variable
func NewLogglyAdapter(route *router.Route) (router.LogAdapter, error) {
	token := os.Getenv(logglyTokenEnvVar)

	if token == "" {
		return nil, errors.New("Could not find environment variable LOGGLY_TOKEN")
	}

	return &Adapter{
		token:  token,
		client: http.Client{},
	}, nil
}

// Adapter satisfies the router.LogAdapter interface by providing Stream which
// passes all messages to loggly.
type Adapter struct {
	token  string
	client http.Client
}

// Stream satisfies the router.LogAdapter interface and passes all messages to Loggly
func (l *Adapter) Stream(logstream chan *router.Message) {
	for m := range logstream {
		msg := logglyMessage{
			Message:           m.Data,
			ContainerName:     m.Container.Name,
			ContainerID:       m.Container.ID,
			ContainerImage:    m.Container.Config.Image,
			ContainerHostname: m.Container.Config.Hostname,
		}

		err := l.SendMessage(msg)

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

// SendMessage handles creating and sending a request to Loggly. Any errors that occur during that
// process are bubbled up to the caller
func (l *Adapter) SendMessage(msg logglyMessage) error {
	js, err := json.Marshal(msg)

	if err != nil {
		return err
	}

	url := fmt.Sprintf("%s%s/%s", logglyAddr, logglyEventEndpoint, l.token, "tag", os.Getenv(logglyTagsEnvVar))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(js))

	req.Header.Add("X-LOGGLY-TAG", os.Getenv(logglyTagsEnvVar))

	if err != nil {
		return err
	}

	// TODO: possibly use pool of workers to send requests?
	resp, err := l.client.Do(req)

	if err != nil {
		errMsg := fmt.Sprintf("Error from client: %s", err.Error())
		return errors.New(errMsg)
	}

	if resp.StatusCode != http.StatusOK {
		errMsg := fmt.Sprintf("Received a non 200 status code: %s", err.Error())
		return errors.New(errMsg)
	}

	return nil
}

type logglyMessage struct {
	Message           string `json:"message"`
	ContainerName     string `json:"container_name"`
	ContainerID       string `json:"container_id"`
	ContainerImage    string `json:"container_image"`
	ContainerHostname string `json:"hostname"`
}
<|endoftext|>