<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/howeyc/fsnotify"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

const (
	Version         = "0.1.3"
	PidFile         = "watchf.pid"
	Program         = "watchf"
	ContinueOnError = false
)

var quit = make(chan os.Signal, 1)

func main() {
	// command line parsing
	var commands StringSet
	var sensitive time.Duration

	flag.Var(&commands, "c", "Add arbitrary command(repeatable)")
	flag.DurationVar(&sensitive, "t", time.Duration(100)*time.Millisecond, "The time sensitive for avoid execute command frequently(time unit: ns/us/ms/s/m/h)")
	stop := flag.Bool("s", false, "To stop the "+Program+" Daemon(windows is not support)")
	showVersion := flag.Bool("v", false, "show version")

	flag.Usage = func() {
		fmt.Println("Usage:\n  " + Program + " options 'pattern'")
		fmt.Println("Options:")
		flag.PrintDefaults()
		fmt.Println("Variables:")
		fmt.Println("  $f: The filename of changed file")

		fmt.Println("Example 1:")
		fmt.Println("  " + Program + " -c 'go vet' -c 'go test' -c 'go install' '*.go'")
		fmt.Println("Example 2(Daemon):")
		fmt.Println("  " + Program + " -c 'chmod 644 $f' '*.exe' &")
		fmt.Println("  " + Program + " -s")
	}
	flag.Parse()

	if *showVersion {
		fmt.Println("version " + Version)
	}

	if len(commands) == 0 && !*stop {
		flag.Usage()
		os.Exit(-1)
	}

	pattern := "*"
	if len(flag.Args()) > 0 {
		pattern = strings.Trim(strings.Join(flag.Args(), " "), " ")
	}

	// stop daemon via signal
	if *stop {
		daemon := &Daemon{}
		if err := daemon.Stop(); err != nil {
			fmt.Printf("cannot stop process:%d caused by:\n%s\n", daemon.pid, err)
		}
		return
	}

	// start daemon
	daemon := &Daemon{
		service: &WatchService{pattern, sensitive, commands, nil},
	}

	err := daemon.Start()
	checkError(err)

	// stop daemon
	waitForStop(daemon)
}

type Service interface {
	start() error
	stop() error
}

type Daemon struct {
	local   bool
	pid     int
	service Service
}

func (d *Daemon) Start() (err error) {
	if d.IsRunning() {
		log.Fatalln(Program + " is already running")
		return
	} else {
		if err = ioutil.WriteFile(PidFile, []byte(strconv.Itoa(os.Getpid())), 0644); err != nil {
			return
		}
		d.local = true
		return d.service.start()
	}
}

func (d *Daemon) Stop() (err error) {
	if d.IsRunning() {
		if d.local {
			os.Remove(PidFile)
			return d.service.stop()
		} else {
			var process *os.Process
			process, err = os.FindProcess(d.pid)
			if err != nil {
				return
			}
			err = process.Signal(os.Interrupt)
		}
	}
	return
}

func (d *Daemon) IsRunning() bool {
	if d.local {
		return true
	}

	var err error
	d.pid, err = getDaemonPid()
	if err == nil {
		return isProcessRunning(d.pid)
	}
	return false
}

func checkError(err error) {
	if err != nil {
		log.Println(err)
		close(quit)
	}
}

func getDaemonPid() (pid int, err error) {
	data, err := ioutil.ReadFile(PidFile)
	if err == nil {
		pid, err = strconv.Atoi(string(data))
	}
	return
}

func waitForStop(daemon *Daemon) {

	signal.Notify(quit, os.Kill, os.Interrupt)

	<-quit
	if err := daemon.Stop(); err != nil {
		fmt.Printf(Program+" stop failed: %s\n", err)
	} else {
		fmt.Println(Program + " stopped")
	}
}

type WatchService struct {
	pattern   string
	sensitive time.Duration
	commands  []string
	watcher   *fsnotify.Watcher
}

const (
	TRUE  int32 = 1
	FALSE int32 = 0
)

func (w *WatchService) start() (err error) {
	w.watcher, err = fsnotify.NewWatcher()
	if err != nil {
		return
	}

	go func() {
		var running = FALSE
		var lastExec time.Time

		for {
			select {
			case evt, ok := <-w.watcher.Event:
				if ok {
					now := time.Now()
					// TODO: verify file change by size and checksum
					// TODO: accept specific event
					if atomic.LoadInt32(&running) != TRUE && acceptedFile(w.pattern, evt) && lastExec.Add(w.sensitive).Before(now) {
						lastExec = now
						// using another goroutine to run command in order to non-blocking watcher.Event channel
						go execute(w.commands, evt, &running)
					}
				} else {
					return
				}
			case err, ok := <-w.watcher.Error:
				if ok {
					checkError(err)
				} else {
					return
				}
			}
		}
	}()

	// TODO: watching subdirectory
	err = w.watcher.Watch(".")
	return
}

func (w *WatchService) stop() error {
	return w.watcher.Close()
}

func acceptedFile(pattern string, ev *fsnotify.FileEvent) bool {
	matched, err := filepath.Match(pattern, ev.Name[2:])
	checkError(err)
	return matched
}

func execute(commands []string, evt *fsnotify.FileEvent, running *int32) {
	atomic.StoreInt32(running, TRUE)
	for _, command := range commands {
		command := applyCustomVariable(command, evt)
		// THINK: support command with pipeline

		args := strings.Split(command, " ")
		var cmd *exec.Cmd

		if len(args) > 1 {
			cmd = exec.Command(args[0], args[1:]...)
		} else {
			cmd = exec.Command(args[0])
		}

		if err := runCommand(cmd); err != nil && !ContinueOnError {
			break
		}
	}
	atomic.StoreInt32(running, FALSE)
}

func applyCustomVariable(command string, evt *fsnotify.FileEvent) string {
	return strings.Replace(command, "$f", evt.Name, -1)
}

func runCommand(cmd *exec.Cmd) (err error) {
	writer := &bytes.Buffer{}
	cmd.Stderr = writer
	cmd.Stdout = writer

	if err = cmd.Run(); err != nil {
		log.Printf("run \"%s\" failed, err: %s\n", strings.Join(cmd.Args, " "), err)
	}

	fmt.Println(string(writer.Bytes()))
	return
}

type StringSet []string

func (f *StringSet) String() string {
	return fmt.Sprint([]string(*f))
}

func (f *StringSet) Set(value string) error {
	*f = append(*f, value)
	return nil
}
<commit_msg>fix printing blank lines<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"github.com/howeyc/fsnotify"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"sync/atomic"
	"time"
)

const (
	Version         = "0.1.3"
	PidFile         = "watchf.pid"
	Program         = "watchf"
	ContinueOnError = false
)

var quit = make(chan os.Signal, 1)

func main() {
	// command line parsing
	var commands StringSet
	var sensitive time.Duration

	flag.Var(&commands, "c", "Add arbitrary command(repeatable)")
	flag.DurationVar(&sensitive, "t", time.Duration(100)*time.Millisecond, "The time sensitive for avoid execute command frequently(time unit: ns/us/ms/s/m/h)")
	stop := flag.Bool("s", false, "To stop the "+Program+" Daemon(windows is not support)")
	showVersion := flag.Bool("v", false, "show version")

	flag.Usage = func() {
		fmt.Println("Usage:\n  " + Program + " options 'pattern'")
		fmt.Println("Options:")
		flag.PrintDefaults()
		fmt.Println("Variables:")
		fmt.Println("  $f: The filename of changed file")

		fmt.Println("Example 1:")
		fmt.Println("  " + Program + " -c 'go vet' -c 'go test' -c 'go install' '*.go'")
		fmt.Println("Example 2(Daemon):")
		fmt.Println("  " + Program + " -c 'chmod 644 $f' '*.exe' &")
		fmt.Println("  " + Program + " -s")
	}
	flag.Parse()

	if *showVersion {
		fmt.Println("version " + Version)
	}

	if len(commands) == 0 && !*stop {
		flag.Usage()
		os.Exit(-1)
	}

	pattern := "*"
	if len(flag.Args()) > 0 {
		pattern = strings.Trim(strings.Join(flag.Args(), " "), " ")
	}

	// stop daemon via signal
	if *stop {
		daemon := &Daemon{}
		if err := daemon.Stop(); err != nil {
			fmt.Printf("cannot stop process:%d caused by:\n%s\n", daemon.pid, err)
		}
		return
	}

	// start daemon
	daemon := &Daemon{
		service: &WatchService{pattern, sensitive, commands, nil},
	}

	err := daemon.Start()
	checkError(err)

	// stop daemon
	waitForStop(daemon)
}

type Service interface {
	start() error
	stop() error
}

type Daemon struct {
	local   bool
	pid     int
	service Service
}

func (d *Daemon) Start() (err error) {
	if d.IsRunning() {
		log.Fatalln(Program + " is already running")
		return
	} else {
		if err = ioutil.WriteFile(PidFile, []byte(strconv.Itoa(os.Getpid())), 0644); err != nil {
			return
		}
		d.local = true
		return d.service.start()
	}
}

func (d *Daemon) Stop() (err error) {
	if d.IsRunning() {
		if d.local {
			os.Remove(PidFile)
			return d.service.stop()
		} else {
			var process *os.Process
			process, err = os.FindProcess(d.pid)
			if err != nil {
				return
			}
			err = process.Signal(os.Interrupt)
		}
	}
	return
}

func (d *Daemon) IsRunning() bool {
	if d.local {
		return true
	}

	var err error
	d.pid, err = getDaemonPid()
	if err == nil {
		return isProcessRunning(d.pid)
	}
	return false
}

func checkError(err error) {
	if err != nil {
		log.Println(err)
		close(quit)
	}
}

func getDaemonPid() (pid int, err error) {
	data, err := ioutil.ReadFile(PidFile)
	if err == nil {
		pid, err = strconv.Atoi(string(data))
	}
	return
}

func waitForStop(daemon *Daemon) {

	signal.Notify(quit, os.Kill, os.Interrupt)

	<-quit
	if err := daemon.Stop(); err != nil {
		fmt.Printf(Program+" stop failed: %s\n", err)
	} else {
		fmt.Println(Program + " stopped")
	}
}

type WatchService struct {
	pattern   string
	sensitive time.Duration
	commands  []string
	watcher   *fsnotify.Watcher
}

const (
	TRUE  int32 = 1
	FALSE int32 = 0
)

func (w *WatchService) start() (err error) {
	w.watcher, err = fsnotify.NewWatcher()
	if err != nil {
		return
	}

	go func() {
		var running = FALSE
		var lastExec time.Time

		for {
			select {
			case evt, ok := <-w.watcher.Event:
				if ok {
					now := time.Now()
					// TODO: verify file change by size and checksum
					// TODO: accept specific event
					if atomic.LoadInt32(&running) != TRUE && acceptedFile(w.pattern, evt) && lastExec.Add(w.sensitive).Before(now) {
						lastExec = now
						// using another goroutine to run command in order to non-blocking watcher.Event channel
						go execute(w.commands, evt, &running)
					}
				} else {
					return
				}
			case err, ok := <-w.watcher.Error:
				if ok {
					checkError(err)
				} else {
					return
				}
			}
		}
	}()

	// TODO: watching subdirectory
	err = w.watcher.Watch(".")
	return
}

func (w *WatchService) stop() error {
	return w.watcher.Close()
}

func acceptedFile(pattern string, ev *fsnotify.FileEvent) bool {
	matched, err := filepath.Match(pattern, ev.Name[2:])
	checkError(err)
	return matched
}

func execute(commands []string, evt *fsnotify.FileEvent, running *int32) {
	atomic.StoreInt32(running, TRUE)
	for _, command := range commands {
		command := applyCustomVariable(command, evt)
		// THINK: support command with pipeline

		args := strings.Split(command, " ")
		var cmd *exec.Cmd

		if len(args) > 1 {
			cmd = exec.Command(args[0], args[1:]...)
		} else {
			cmd = exec.Command(args[0])
		}

		if err := runCommand(cmd); err != nil && !ContinueOnError {
			break
		}
	}
	atomic.StoreInt32(running, FALSE)
}

func applyCustomVariable(command string, evt *fsnotify.FileEvent) string {
	return strings.Replace(command, "$f", evt.Name, -1)
}

func runCommand(cmd *exec.Cmd) (err error) {
	buffer := &bytes.Buffer{}
	cmd.Stderr = buffer
	cmd.Stdout = buffer

	if err = cmd.Run(); err != nil {
		log.Printf("run \"%s\" failed, err: %s\n", strings.Join(cmd.Args, " "), err)
	}

	if len(buffer.Bytes()) > 0 {
		fmt.Println(string(buffer.Bytes()))
	}
	return
}

type StringSet []string

func (f *StringSet) String() string {
	return fmt.Sprint([]string(*f))
}

func (f *StringSet) Set(value string) error {
	*f = append(*f, value)
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 IBM Corporation
//
//   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 eureka

import (
	"encoding/json"
)

type appVersion struct {
	VersionDelta int64  `json:"versions__delta,omitempty"`
	Hashcode     string `json:"apps__hashcode,omitempty"`
}

// Applications is an array of application objects
type Applications struct {
	appVersion
	Application []*Application `json:"application,omitempty"`
}

// UnmarshalJSON parses the JSON object of Applications struct.
// We need this specific implementation because the Eureka server
// marshals differently single application (object) and multiple applications (array).
func (apps *Applications) UnmarshalJSON(b []byte) error {
	type singleApplications struct {
		appVersion
		Application *Application `json:"application,omitempty"`
	}

	type multiApplications struct {
		appVersion
		Application []*Application `json:"application,omitempty"`
	}

	var mApps multiApplications
	err := json.Unmarshal(b, &mApps)
	if err != nil {
		// error probably means that we have a single Application object.
		// Thus, we try to unmarshal to a different object type
		var sApps singleApplications
		err = json.Unmarshal(b, &sApps)
		if err != nil {
			return err
		}
		apps.Hashcode = sApps.Hashcode
		apps.VersionDelta = sApps.VersionDelta
		if sApps.Application != nil {
			apps.Application = []*Application{sApps.Application}
		}
		return nil
	}

	apps.Hashcode = mApps.Hashcode
	apps.VersionDelta = mApps.VersionDelta
	apps.Application = mApps.Application
	return nil
}
<commit_msg>fix eureka app struct (#559)<commit_after>// Copyright 2016 IBM Corporation
//
//   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 eureka

import (
	"encoding/json"
)

type appVersion struct {
	VersionDelta int64  `json:"versions__delta,omitempty,string"`
	Hashcode     string `json:"apps__hashcode,omitempty"`
}

// Applications is an array of application objects
type Applications struct {
	appVersion
	Application []*Application `json:"application,omitempty"`
}

// UnmarshalJSON parses the JSON object of Applications struct.
// We need this specific implementation because the Eureka server
// marshals differently single application (object) and multiple applications (array).
func (apps *Applications) UnmarshalJSON(b []byte) error {
	type singleApplications struct {
		appVersion
		Application *Application `json:"application,omitempty"`
	}

	type multiApplications struct {
		appVersion
		Application []*Application `json:"application,omitempty"`
	}

	var mApps multiApplications
	err := json.Unmarshal(b, &mApps)
	if err != nil {
		// error probably means that we have a single Application object.
		// Thus, we try to unmarshal to a different object type
		var sApps singleApplications
		err = json.Unmarshal(b, &sApps)
		if err != nil {
			return err
		}
		apps.Hashcode = sApps.Hashcode
		apps.VersionDelta = sApps.VersionDelta
		if sApps.Application != nil {
			apps.Application = []*Application{sApps.Application}
		}
		return nil
	}

	apps.Hashcode = mApps.Hashcode
	apps.VersionDelta = mApps.VersionDelta
	apps.Application = mApps.Application
	return nil
}
<|endoftext|>


<commit_before>package object

import (
	"encoding/hex"
	"fmt"
	"strings"
)

// ID is an identifier of a repository object. Repository objects can be stored.
//
// 1. In a single content block, this is the most common case for small objects.
// 2. In a series of content blocks with an indirect block pointing at them (multiple indirections are allowed).
//    This is used for larger files. Object IDs using indirect blocks start with "I"
type ID string

// HasObjectID exposes the identifier of an object.
type HasObjectID interface {
	ObjectID() ID
}

// String returns string representation of ObjectID that is suitable for displaying in the UI.
func (i ID) String() string {
	return string(i)
}

// IndexObjectID returns the object ID of the underlying index object.
func (i ID) IndexObjectID() (ID, bool) {
	if strings.HasPrefix(string(i), "I") {
		return i[1:], true
	}

	return "", false
}

// BlockID returns the block ID of the underlying content storage block.
func (i ID) BlockID() (string, bool) {
	if strings.HasPrefix(string(i), "D") {
		return string(i[1:]), true
	}
	if strings.HasPrefix(string(i), "I") {
		return "", false
	}

	return string(i), true
}

// Validate checks the ID format for validity and reports any errors.
func (i ID) Validate() error {
	if indexObjectID, ok := i.IndexObjectID(); ok {
		if err := indexObjectID.Validate(); err != nil {
			return fmt.Errorf("invalid indirect object ID %v: %v", i, err)
		}

		return nil
	}

	if blockID, ok := i.BlockID(); ok {
		if len(blockID) < 2 {
			return fmt.Errorf("missing block ID")
		}

		// odd length - firstcharacter must be a single character between 'g' and 'z'
		if len(blockID)%2 == 1 {
			if blockID[0] < 'g' || blockID[0] > 'z' {
				return fmt.Errorf("invalid block ID prefix: %v", blockID)
			}
			blockID = blockID[1:]
		}

		if _, err := hex.DecodeString(blockID); err != nil {
			return fmt.Errorf("invalid blockID suffix, must be base-16 encoded: %v", blockID)
		}

		return nil
	}

	return fmt.Errorf("invalid object ID: %v", i)
}

// DirectObjectID returns direct object ID based on the provided block ID.
func DirectObjectID(blockID string) ID {
	return ID(blockID)
}

// IndirectObjectID returns indirect object ID based on the underlying index object ID.
func IndirectObjectID(indexObjectID ID) ID {
	return "I" + indexObjectID
}

// ParseID converts the specified string into object ID
func ParseID(s string) (ID, error) {
	i := ID(s)
	return i, i.Validate()
}
<commit_msg>present legacy object IDs without the 'D'<commit_after>package object

import (
	"encoding/hex"
	"fmt"
	"strings"
)

// ID is an identifier of a repository object. Repository objects can be stored.
//
// 1. In a single content block, this is the most common case for small objects.
// 2. In a series of content blocks with an indirect block pointing at them (multiple indirections are allowed).
//    This is used for larger files. Object IDs using indirect blocks start with "I"
type ID string

// HasObjectID exposes the identifier of an object.
type HasObjectID interface {
	ObjectID() ID
}

// String returns string representation of ObjectID that is suitable for displaying in the UI.
func (i ID) String() string {
	return strings.Replace(string(i), "D", "", -1)
}

// IndexObjectID returns the object ID of the underlying index object.
func (i ID) IndexObjectID() (ID, bool) {
	if strings.HasPrefix(string(i), "I") {
		return i[1:], true
	}

	return "", false
}

// BlockID returns the block ID of the underlying content storage block.
func (i ID) BlockID() (string, bool) {
	if strings.HasPrefix(string(i), "D") {
		return string(i[1:]), true
	}
	if strings.HasPrefix(string(i), "I") {
		return "", false
	}

	return string(i), true
}

// Validate checks the ID format for validity and reports any errors.
func (i ID) Validate() error {
	if indexObjectID, ok := i.IndexObjectID(); ok {
		if err := indexObjectID.Validate(); err != nil {
			return fmt.Errorf("invalid indirect object ID %v: %v", i, err)
		}

		return nil
	}

	if blockID, ok := i.BlockID(); ok {
		if len(blockID) < 2 {
			return fmt.Errorf("missing block ID")
		}

		// odd length - firstcharacter must be a single character between 'g' and 'z'
		if len(blockID)%2 == 1 {
			if blockID[0] < 'g' || blockID[0] > 'z' {
				return fmt.Errorf("invalid block ID prefix: %v", blockID)
			}
			blockID = blockID[1:]
		}

		if _, err := hex.DecodeString(blockID); err != nil {
			return fmt.Errorf("invalid blockID suffix, must be base-16 encoded: %v", blockID)
		}

		return nil
	}

	return fmt.Errorf("invalid object ID: %v", i)
}

// DirectObjectID returns direct object ID based on the provided block ID.
func DirectObjectID(blockID string) ID {
	return ID(blockID)
}

// IndirectObjectID returns indirect object ID based on the underlying index object ID.
func IndirectObjectID(indexObjectID ID) ID {
	return "I" + indexObjectID
}

// ParseID converts the specified string into object ID
func ParseID(s string) (ID, error) {
	i := ID(s)
	return i, i.Validate()
}
<|endoftext|>


<commit_before>package broker

import (
	"encoding/json"
	"fmt"
	"testing"
	"time"

	"h12.me/kafka/proto"
)

func TestMeta(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32791",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	req := &proto.Request{
		APIKey:        proto.TopicMetadataRequestType,
		APIVersion:    0,
		CorrelationID: 1,
		ClientID:      "abc",
		RequestMessage: &proto.TopicMetadataRequest{
			"test",
		},
	}
	resp := &proto.Response{
		ResponseMessage: &proto.TopicMetadataResponse{},
	}
	if err := broker.Do(req, resp); err != nil {
		t.Fatal(t)
	}
	fmt.Println(toJSON(resp.ResponseMessage))
}

func TestOffsetCommit(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32791",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	tm := time.Now()
	req := &proto.Request{
		APIKey:        proto.OffsetCommitRequestType,
		APIVersion:    1,
		CorrelationID: 1,
		ClientID:      "abc",
		RequestMessage: &proto.OffsetCommitRequestV1{
			ConsumerGroupID:           "test-1",
			ConsumerGroupGenerationID: 1,
			ConsumerID:                "consumer-1",
			OffsetCommitInTopicV1s: []proto.OffsetCommitInTopicV1{
				{
					TopicName: "test",
					OffsetCommitInPartitionV1s: []proto.OffsetCommitInPartitionV1{
						{
							Partition: 0,
							Offset:    1,
							TimeStamp: tm.Unix(),
							Metadata:  fmt.Sprint(tm.Unix()),
						},
					},
				},
			},
		},
	}
	resp := proto.OffsetCommitResponse{}
	if err := broker.Do(req, &proto.Response{ResponseMessage: &resp}); err != nil {
		t.Fatal(t)
	}
	fmt.Println(toJSON(resp))
}

func TestOffsetFetch(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32791",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	req := &proto.Request{
		APIKey:        proto.OffsetFetchRequestType,
		APIVersion:    1,
		CorrelationID: 1,
		ClientID:      "abc",
		RequestMessage: &proto.OffsetFetchRequest{
			ConsumerGroup: "test-1",
			PartitionInTopics: []proto.PartitionInTopic{
				{
					TopicName:  "test",
					Partitions: []int32{0, 1, 2},
				},
			},
		},
	}
	resp := proto.OffsetFetchResponse{}
	if err := broker.Do(req, &proto.Response{ResponseMessage: &resp}); err != nil {
		t.Fatal(t)
	}
	fmt.Println(toJSON(resp))
}

func TestConsumerMeta(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32791",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	creq := proto.ConsumerMetadataRequest("test-1")
	req := &proto.Request{
		APIKey:         proto.ConsumerMetadataRequestType,
		APIVersion:     0,
		CorrelationID:  1,
		ClientID:       "abc",
		RequestMessage: &creq,
	}
	resp := proto.ConsumerMetadataResponse{}
	if err := broker.Do(req, &proto.Response{ResponseMessage: &resp}); err != nil {
		t.Fatal(t)
	}
	fmt.Println(toJSON(resp))
}

func TestProduce(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32793",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	tm := time.Now()
	req := &proto.Request{
		APIKey:        proto.ProduceRequestType,
		APIVersion:    0,
		CorrelationID: 1,
		ClientID:      "abc",
		RequestMessage: &proto.ProduceRequest{
			RequiredAcks: 1,
			Timeout:      0,
			MessageSetInTopics: []proto.MessageSetInTopic{
				{
					TopicName: "test",
					MessageSetInPartitions: []proto.MessageSetInPartition{
						{
							Partition: 0,
							MessageSet: []proto.OffsetMessage{
								{
									SizedMessage: proto.SizedMessage{CRCMessage: proto.CRCMessage{
										Message: proto.Message{
											Key:   nil,
											Value: []byte("hello " + tm.Format(time.RFC3339)),
										},
									}}},
							},
						},
					},
				},
			},
		},
	}
	resp := proto.ProduceResponse{}
	if err := broker.Do(req, &proto.Response{ResponseMessage: &resp}); err != nil {
		t.Fatal(err)
	}
	fmt.Println(toJSON(resp))
}

func toJSON(v interface{}) string {
	buf, _ := json.MarshalIndent(v, "", "    ")
	return string(buf)
}
<commit_msg>testing consume api.<commit_after>package broker

import (
	"encoding/json"
	"fmt"
	"testing"
	"time"

	"h12.me/kafka/proto"
)

func TestMeta(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32791",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	req := &proto.Request{
		APIKey:        proto.TopicMetadataRequestType,
		APIVersion:    0,
		CorrelationID: 1,
		ClientID:      "abc",
		RequestMessage: &proto.TopicMetadataRequest{
			"test",
		},
	}
	resp := &proto.Response{
		ResponseMessage: &proto.TopicMetadataResponse{},
	}
	if err := broker.Do(req, resp); err != nil {
		t.Fatal(t)
	}
	fmt.Println(toJSON(resp.ResponseMessage))
}

func TestConsumeAll(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32793",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	req := &proto.Request{
		APIKey:        proto.FetchRequestType,
		APIVersion:    0,
		CorrelationID: 1,
		ClientID:      "abc",
		RequestMessage: &proto.FetchRequest{
			ReplicaID: -1,
			MinBytes:  10,
			FetchOffsetInTopics: []proto.FetchOffsetInTopic{
				{
					TopicName: "test",
					FetchOffsetInPartitions: []proto.FetchOffsetInPartition{
						{
							Partition:   0,
							FetchOffset: 0,
						},
					},
				},
			},
		},
	}
	resp := proto.FetchResponse{}
	if err := broker.Do(req, &proto.Response{ResponseMessage: &resp}); err != nil {
		t.Fatal(t)
	}
	fmt.Println(toJSON(resp))
}

func TestOffsetCommit(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32791",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	tm := time.Now()
	req := &proto.Request{
		APIKey:        proto.OffsetCommitRequestType,
		APIVersion:    1,
		CorrelationID: 1,
		ClientID:      "abc",
		RequestMessage: &proto.OffsetCommitRequestV1{
			ConsumerGroupID:           "test-1",
			ConsumerGroupGenerationID: 1,
			ConsumerID:                "consumer-1",
			OffsetCommitInTopicV1s: []proto.OffsetCommitInTopicV1{
				{
					TopicName: "test",
					OffsetCommitInPartitionV1s: []proto.OffsetCommitInPartitionV1{
						{
							Partition: 0,
							Offset:    1,
							TimeStamp: tm.Unix(),
							Metadata:  fmt.Sprint(tm.Unix()),
						},
					},
				},
			},
		},
	}
	resp := proto.OffsetCommitResponse{}
	if err := broker.Do(req, &proto.Response{ResponseMessage: &resp}); err != nil {
		t.Fatal(t)
	}
	fmt.Println(toJSON(resp))
}

func TestOffsetFetch(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32791",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	req := &proto.Request{
		APIKey:        proto.OffsetFetchRequestType,
		APIVersion:    1,
		CorrelationID: 1,
		ClientID:      "abc",
		RequestMessage: &proto.OffsetFetchRequest{
			ConsumerGroup: "test-1",
			PartitionInTopics: []proto.PartitionInTopic{
				{
					TopicName:  "test",
					Partitions: []int32{0, 1, 2},
				},
			},
		},
	}
	resp := proto.OffsetFetchResponse{}
	if err := broker.Do(req, &proto.Response{ResponseMessage: &resp}); err != nil {
		t.Fatal(t)
	}
	fmt.Println(toJSON(resp))
}

func TestConsumerMeta(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32791",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	creq := proto.ConsumerMetadataRequest("test-1")
	req := &proto.Request{
		APIKey:         proto.ConsumerMetadataRequestType,
		APIVersion:     0,
		CorrelationID:  1,
		ClientID:       "abc",
		RequestMessage: &creq,
	}
	resp := proto.ConsumerMetadataResponse{}
	if err := broker.Do(req, &proto.Response{ResponseMessage: &resp}); err != nil {
		t.Fatal(t)
	}
	fmt.Println(toJSON(resp))
}

func TestProduce(t *testing.T) {
	broker, err := New(&Config{
		Addr:         "docker:32793",
		SendQueueLen: 10,
		RecvQueueLen: 10,
	})
	if err != nil {
		t.Fatal(err)
	}
	tm := time.Now()
	req := &proto.Request{
		APIKey:        proto.ProduceRequestType,
		APIVersion:    0,
		CorrelationID: 1,
		ClientID:      "abc",
		RequestMessage: &proto.ProduceRequest{
			RequiredAcks: 1,
			Timeout:      0,
			MessageSetInTopics: []proto.MessageSetInTopic{
				{
					TopicName: "test",
					MessageSetInPartitions: []proto.MessageSetInPartition{
						{
							Partition: 0,
							MessageSet: []proto.OffsetMessage{
								{
									SizedMessage: proto.SizedMessage{CRCMessage: proto.CRCMessage{
										Message: proto.Message{
											Key:   nil,
											Value: []byte("hello " + tm.Format(time.RFC3339)),
										},
									}}},
							},
						},
					},
				},
			},
		},
	}
	resp := proto.ProduceResponse{}
	if err := broker.Do(req, &proto.Response{ResponseMessage: &resp}); err != nil {
		t.Fatal(err)
	}
	fmt.Println(toJSON(resp))
}

func toJSON(v interface{}) string {
	buf, _ := json.MarshalIndent(v, "", "    ")
	return string(buf)
}
<|endoftext|>


<commit_before>// Package zlmgo provides Go bindings to the Zen License Manager (ZLM).
package zlmgo

/*
#cgo CFLAGS: -I/usr/local/include
#cgo LDFLAGS: /usr/local/lib/libzlm.a

#include <stdlib.h>
#include <zlm.h>

char zlmgo_errbuf_array[ZLM_ERRBUF];
char* zlmgo_errbuf = zlmgo_errbuf_array; // hack around cgo warning
*/
import "C"

import (
	"errors"
	"runtime"
	"unsafe"
)

type License struct {
	l *C.ZlmLicense
}

// LicenseNew returns a new license object (panics if not enough memory is available).
func LicenseNew() *License {
	license := &License{C.zlm_license_new(C.zlmgo_errbuf)}
	if license.l == nil {
		panic(C.GoString(C.zlmgo_errbuf))
	}
	runtime.SetFinalizer(license, (*License).free)
	return license
}

func (license *License) Get(product, version, argv0, path, licenseString string) error {
	var c_product, c_version, c_argv0, c_path, c_license_string *C.char
	// convert argument to C strings
	if product != "" {
		c_product = C.CString(product)
	}
	if version != "" {
		c_version = C.CString(version)
	}
	if argv0 != "" {
		c_argv0 = C.CString(argv0)
	}
	if path != "" {
		c_path = C.CString(path)
	}
	if licenseString != "" {
		c_license_string = C.CString(licenseString)
	}
	// call actual method
	if C.zlm_license_get(license.l, c_product, c_version, c_argv0, c_path, c_license_string, C.zlmgo_errbuf) != C.ZLM_OK {
		return errors.New(C.GoString(C.zlmgo_errbuf))
	}
	return nil
}

func (license *License) free() {
	C.zlm_license_free(license.l)
}

func (license *License) Product() string {
	return C.GoString(C.zlm_license_product(license.l))
}

func (license *License) Expiry() string {
	return C.GoString(C.zlm_license_expiry(license.l))
}

func (license *License) ExpiryDays() int {
	return int(C.zlm_license_expiry_days(license.l))
}

func (license *License) Customer() string {
	return C.GoString(C.zlm_license_customer(license.l))
}

func (license *License) Userdata() string {
	return C.GoString(C.zlm_license_userdata(license.l))
}

func (license *License) Next() error {
	if C.zlm_license_next(license.l, C.zlmgo_errbuf) != C.ZLM_OK {
		return errors.New(C.GoString(C.zlmgo_errbuf))
	}
	return nil
}

func Version() string {
	return C.GoString(C.zlm_version())
}

func HostidJSON() (string, error) {
	cs := C.zlm_hostid_json(C.zlmgo_errbuf)
	if cs == nil {
		return "", errors.New(C.GoString(C.zlmgo_errbuf))
	}
	hostid := C.GoString(cs)
	C.free(unsafe.Pointer(cs))
	return hostid, nil
}

func (license *License) CheckA() {
	C.zlm_license_check_a(license.l)
}

func (license *License) CheckB() {
	C.zlm_license_check_b(license.l)
}

func (license *License) CheckC() {
	C.zlm_license_check_c(license.l)
}

func (license *License) CheckD() {
	C.zlm_license_check_d(license.l)
}

func (license *License) CheckE() {
	C.zlm_license_check_e(license.l)
}

func (license *License) CheckF() {
	C.zlm_license_check_f(license.l)
}

func (license *License) CheckG() {
	C.zlm_license_check_g(license.l)
}

func (license *License) CheckH() {
	C.zlm_license_check_h(license.l)
}

func (license *License) CheckI() {
	C.zlm_license_check_i(license.l)
}

func (license *License) CheckJ() {
	C.zlm_license_check_j(license.l)
}

func (license *License) CheckK() {
	C.zlm_license_check_k(license.l)
}

func (license *License) CheckL() {
	C.zlm_license_check_l(license.l)
}

func (license *License) CheckM() {
	C.zlm_license_check_m(license.l)
}

func (license *License) CheckN() {
	C.zlm_license_check_n(license.l)
}

func (license *License) CheckO() {
	C.zlm_license_check_o(license.l)
}

func (license *License) CheckP() {
	C.zlm_license_check_p(license.l)
}

func (license *License) CheckQ() {
	C.zlm_license_check_q(license.l)
}

func (license *License) CheckR() {
	C.zlm_license_check_r(license.l)
}
<commit_msg>add documentation<commit_after>// Package zlmgo provides Go bindings to the Zen License Manager (ZLM).
package zlmgo

/*
#cgo CFLAGS: -I/usr/local/include
#cgo LDFLAGS: /usr/local/lib/libzlm.a

#include <stdlib.h>
#include <zlm.h>

char zlmgo_errbuf_array[ZLM_ERRBUF];
char* zlmgo_errbuf = zlmgo_errbuf_array; // hack around cgo warning
*/
import "C"

import (
	"errors"
	"runtime"
	"unsafe"
)

// License wraps a ZLM license object.
type License struct {
	l *C.ZlmLicense
}

// LicenseNew returns a new license object (panics if not enough memory is available).
func LicenseNew() *License {
	license := &License{C.zlm_license_new(C.zlmgo_errbuf)}
	if license.l == nil {
		panic(C.GoString(C.zlmgo_errbuf))
	}
	runtime.SetFinalizer(license, (*License).free)
	return license
}

// Get wraps zlm_license_get(), see
// https://zenlicensemanager.com/documentation/#API
func (license *License) Get(product, version, argv0, path, licenseString string) error {
	var c_product, c_version, c_argv0, c_path, c_license_string *C.char
	// convert argument to C strings
	if product != "" {
		c_product = C.CString(product)
	}
	if version != "" {
		c_version = C.CString(version)
	}
	if argv0 != "" {
		c_argv0 = C.CString(argv0)
	}
	if path != "" {
		c_path = C.CString(path)
	}
	if licenseString != "" {
		c_license_string = C.CString(licenseString)
	}
	// call actual method
	if C.zlm_license_get(license.l, c_product, c_version, c_argv0, c_path, c_license_string, C.zlmgo_errbuf) != C.ZLM_OK {
		return errors.New(C.GoString(C.zlmgo_errbuf))
	}
	return nil
}

func (license *License) free() {
	C.zlm_license_free(license.l)
}

func (license *License) Product() string {
	return C.GoString(C.zlm_license_product(license.l))
}

func (license *License) Expiry() string {
	return C.GoString(C.zlm_license_expiry(license.l))
}

func (license *License) ExpiryDays() int {
	return int(C.zlm_license_expiry_days(license.l))
}

func (license *License) Customer() string {
	return C.GoString(C.zlm_license_customer(license.l))
}

func (license *License) Userdata() string {
	return C.GoString(C.zlm_license_userdata(license.l))
}

func (license *License) Next() error {
	if C.zlm_license_next(license.l, C.zlmgo_errbuf) != C.ZLM_OK {
		return errors.New(C.GoString(C.zlmgo_errbuf))
	}
	return nil
}

func Version() string {
	return C.GoString(C.zlm_version())
}

func HostidJSON() (string, error) {
	cs := C.zlm_hostid_json(C.zlmgo_errbuf)
	if cs == nil {
		return "", errors.New(C.GoString(C.zlmgo_errbuf))
	}
	hostid := C.GoString(cs)
	C.free(unsafe.Pointer(cs))
	return hostid, nil
}

func (license *License) CheckA() {
	C.zlm_license_check_a(license.l)
}

func (license *License) CheckB() {
	C.zlm_license_check_b(license.l)
}

func (license *License) CheckC() {
	C.zlm_license_check_c(license.l)
}

func (license *License) CheckD() {
	C.zlm_license_check_d(license.l)
}

func (license *License) CheckE() {
	C.zlm_license_check_e(license.l)
}

func (license *License) CheckF() {
	C.zlm_license_check_f(license.l)
}

func (license *License) CheckG() {
	C.zlm_license_check_g(license.l)
}

func (license *License) CheckH() {
	C.zlm_license_check_h(license.l)
}

func (license *License) CheckI() {
	C.zlm_license_check_i(license.l)
}

func (license *License) CheckJ() {
	C.zlm_license_check_j(license.l)
}

func (license *License) CheckK() {
	C.zlm_license_check_k(license.l)
}

func (license *License) CheckL() {
	C.zlm_license_check_l(license.l)
}

func (license *License) CheckM() {
	C.zlm_license_check_m(license.l)
}

func (license *License) CheckN() {
	C.zlm_license_check_n(license.l)
}

func (license *License) CheckO() {
	C.zlm_license_check_o(license.l)
}

func (license *License) CheckP() {
	C.zlm_license_check_p(license.l)
}

func (license *License) CheckQ() {
	C.zlm_license_check_q(license.l)
}

func (license *License) CheckR() {
	C.zlm_license_check_r(license.l)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
)

// Token identifies the type of lex items.
type Token int

const (
	// EOF represents the end of file
	EOF Token = iota
	// Error represents an error
	Error
	// Assign represents the assignment '='
	Assign
	// Number represents a simple number
	Number
	// Operator an operator such as '+' '-' '*'
	Operator
	// Asterix the multiplication operator
	Asterix
	// Space represents space separation between tokens
	Space
	// Identifier represent an identifier such as a var name
	Identifier
)

// eof rune to treat EOF like any other character
var eof = rune(0)

func isWhitespace(r rune) bool {
	return r == ' ' || r == '\t' || r == 'n'
}

func isLetter(r rune) bool {
	return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
}

func isDigit(r rune) bool {
	return (r >= '0' && r <= '9')
}

// Scanner represents a lexical scanner
type Scanner struct {
	r *bufio.Reader
}

// NewScanner returns a new instance of Scanner.
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{r: bufio.NewReader(r)}
}

// read reads the next rune from the bufferred reader.
// Returns the rune(0) if an error occurs (or io.EOF is returned)
func (s *Scanner) read() rune {
	r, _, err := s.r.ReadRune()
	if err != nil {
		return eof
	}
	return r
}

func (s *Scanner) unread() { _ = s.r.UnreadRune() }

// Scan retusn the next token and literal value.
func (s *Scanner) Scan() (t Token, lit string) {
	// read the next rune.
	r := s.read()

	// if we see whitespace then consume all contiguous whitespace.
	// if we see a letter then consume as an identifier keyword word.
	if isWhitespace(r) {
		s.unread()
		return s.scanWhitespace()
	} else if isLetter(r) {
		s.unread()
		return s.scanIdentifier()
	}

	switch r {
	case eof:
		return EOF, ""
	case '*':
		return Asterix, string(r)
	}
	return Error, string(r)
}

// scanWhitespace consumes the current rune and all contiguous whitespace.
func (s *Scanner) scanWhitespace() (t Token, lit string) {
	// Create a buffer and read the current character into it.
	var buf bytes.Buffer
	buf.WriteRune(s.read())

	// Read every subsequent whitespace character into the bufer.
	// non whitespace characters and EOF will cause the loop to exit.
	for {
		if r := s.read(); r == eof {
			break
		} else if !isWhitespace(r) {
			s.unread()
			break
		} else {
			buf.WriteRune(r)
		}
	}
	return Space, buf.String()
}

func (s *Scanner) scanIdentifier() (t Token, lit string) {
	var buf bytes.Buffer
	buf.WriteRune(s.read())

	for {
		if r := s.read(); r == eof {
			break
		} else if !isLetter(r) && !isDigit(r) && r != '_' {
			s.unread()
			break
		} else {
			_, _ = buf.WriteRune(r)
		}
	}
	return Identifier, buf.String()
}

// Parser represents a parser
type Parser struct {
	s   *Scanner
	buf struct {
		t   Token  // last read token
		lit string // last read literal
		n   int    // buffer size (max=1)
	}
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *Parser {
	return &Parser{s: NewScanner(r)}
}

func main() {
	fmt.Println("hello, world")
}
<commit_msg>add parser scan and unscan methods<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
)

// Token identifies the type of lex items.
type Token int

const (
	// EOF represents the end of file
	EOF Token = iota
	// Error represents an error
	Error
	// Assign represents the assignment '='
	Assign
	// Number represents a simple number
	Number
	// Operator an operator such as '+' '-' '*'
	Operator
	// Asterix the multiplication operator
	Asterix
	// Space represents space separation between tokens
	Space
	// Identifier represent an identifier such as a var name
	Identifier
)

// eof rune to treat EOF like any other character
var eof = rune(0)

func isWhitespace(r rune) bool {
	return r == ' ' || r == '\t' || r == 'n'
}

func isLetter(r rune) bool {
	return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z')
}

func isDigit(r rune) bool {
	return (r >= '0' && r <= '9')
}

// Scanner represents a lexical scanner
type Scanner struct {
	r *bufio.Reader
}

// NewScanner returns a new instance of Scanner.
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{r: bufio.NewReader(r)}
}

// read reads the next rune from the bufferred reader.
// Returns the rune(0) if an error occurs (or io.EOF is returned)
func (s *Scanner) read() rune {
	r, _, err := s.r.ReadRune()
	if err != nil {
		return eof
	}
	return r
}

func (s *Scanner) unread() { _ = s.r.UnreadRune() }

// Scan retusn the next token and literal value.
func (s *Scanner) Scan() (t Token, lit string) {
	// read the next rune.
	r := s.read()

	// if we see whitespace then consume all contiguous whitespace.
	// if we see a letter then consume as an identifier keyword word.
	if isWhitespace(r) {
		s.unread()
		return s.scanWhitespace()
	} else if isLetter(r) {
		s.unread()
		return s.scanIdentifier()
	}

	switch r {
	case eof:
		return EOF, ""
	case '*':
		return Asterix, string(r)
	}
	return Error, string(r)
}

// scanWhitespace consumes the current rune and all contiguous whitespace.
func (s *Scanner) scanWhitespace() (t Token, lit string) {
	// Create a buffer and read the current character into it.
	var buf bytes.Buffer
	buf.WriteRune(s.read())

	// Read every subsequent whitespace character into the bufer.
	// non whitespace characters and EOF will cause the loop to exit.
	for {
		if r := s.read(); r == eof {
			break
		} else if !isWhitespace(r) {
			s.unread()
			break
		} else {
			buf.WriteRune(r)
		}
	}
	return Space, buf.String()
}

func (s *Scanner) scanIdentifier() (t Token, lit string) {
	var buf bytes.Buffer
	buf.WriteRune(s.read())

	for {
		if r := s.read(); r == eof {
			break
		} else if !isLetter(r) && !isDigit(r) && r != '_' {
			s.unread()
			break
		} else {
			_, _ = buf.WriteRune(r)
		}
	}
	return Identifier, buf.String()
}

// Parser represents a parser
type Parser struct {
	s   *Scanner
	buf struct {
		t   Token  // last read token
		lit string // last read literal
		n   int    // buffer size (max=1)
	}
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *Parser {
	return &Parser{s: NewScanner(r)}
}

// scan returns the next token from the underlying scanner.
// if a token has been unscanned then read that instead.
func (p *Parser) scan() (t Token, lit string) {
	// if we have a token on the buffer, then return it
	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.t, p.buf.lit
	}
	// otherwise read the next token from the scanner
	t, lit = p.s.Scan()

	// save it to the buffer in case we unscan later.
	p.buf.t, p.buf.lit = t, lit
	return
}

// unscan pushes the previously read token back onto the buffer.
func (p *Parser) unscan() { p.buf.n = 1 }

// scanIgnoreWhitespace scans the next non-whitespace token.
func (p *Parser) scanIgnoreWhitespace() (t Token, lit string) {
	t, lit = p.scan()
	if t == Space {
		t, lit = p.scan()
	}
	return
}

func main() {
	fmt.Println("hello, world")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"io"
	"math"
	"net/http"
	"net/url"
	"strconv"

	"golang.org/x/oauth2"
	"google.golang.org/api/plus/v1"

	"google-musicmanager-go"
)

var scopes = []string{musicmanager.MusicManagerScope, plus.PlusMeScope}
var conf = googleMustConfigFromFile("credentials.json", scopes...)
var tpls = template.Must(template.New("static").
	Funcs(map[string]interface{}{"reverse": reversed}).
	ParseGlob("static/*.tpl"))

func init() {
	http.Handle("/static/", http.FileServer(http.Dir(".")))
	http.Handle("/auth", &REST{Get: auth})
	http.Handle("/oauth2callback", &REST{Get: oauth2callback})
	http.Handle("/register", &REST{
		Init: initMusicManager,
		Get:  register,
	})
	http.Handle("/tracks/", &REST{
		Init:   initMusicManager,
		Get:    tracksGet,
		List:   tracksList,
		Insert: tracksInsert,
	})
	/*
		http.Handle("/jobs/", &REST{
			Init:   initMusicManager,
			List:   jobsList,
			Delete: jobsCancel,
		})
	*/
}

func auth(_ interface{}, w http.ResponseWriter, r *http.Request) error {
	state, err := nonce(32)
	if err != nil {
		return err
	}
	// BUG(lor): Google Play Music Web Manager ignores the
	// "redirect_uris" property of the credentials file.  Instead,
	// a redirect URL is always created dynamically by appending
	// "/oauth2callback" to the scheme and host of the domain on
	// which Google Play Music Web Manager received the request for
	// "/auth".
	conf.RedirectURL = getRedirectURL(getContext(r))
	httpSetCookie(w, r, "state", state)
	httpSetCookie(w, r, "redirect", r.FormValue("redirect"))
	http.Redirect(w, r, conf.AuthCodeURL(state), http.StatusFound)
	return nil
}

func oauth2callback(_ interface{}, w http.ResponseWriter, r *http.Request) error {
	// Confirm that the state matches the nonce we stored
	// (See https://tools.ietf.org/html/rfc6749#section-10.12.)
	rstate := r.FormValue("state")
	astate, err := r.Cookie("state")
	if err != nil || rstate != astate.Value {
		return &RESTError{
			Code: http.StatusBadRequest,
			Message: "state parameter and cookie mismatch" +
				"; have you perhaps disabled cookies?",
		}
	}
	// Exchange the authorization code for an access token.
	c := getContext(r)
	tok, err := conf.Exchange(c, r.FormValue("code"))
	if err != nil {
		return err
	}
	// BUG(lor): Google Play Music does not allow downloading tracks
	// with an uploader_id that is not sufficiently
	// "MAC address-like" (perhaps it only checks for a colon?)
	// The /oauth2callback endpoint generates the uploader_id by
	// injecting a colon between every two digits of the user's
	// Google Account ID, which appears to suffice.
	client := conf.Client(c, tok)
	plus, err := plus.New(client)
	if err != nil {
		return err
	}
	person, err := plus.People.Get("me").Do()
	if err != nil {
		return err
	}
	id, err := regexpReplaceAllString(`(..)`, person.Id, "$1:")
	if err != nil {
		return err
	}
	// Wipe the state and redirect cookies, no longer necessary,
	// and store the access token and uploader ID as cookies.
	httpSetCookie(w, r, "state", "")
	httpSetCookie(w, r, "redirect", "")
	httpSetCookie(w, r, "access_token", tok.AccessToken)
	httpSetCookie(w, r, "uploader_id", id)
	// We autoredirect to the registration endpoint for convenience.
	// If the redirect cookie was provided, remember to tell the
	// registration endpoint to continue there after success.
	registerURL := "/register"
	redirect, err := r.Cookie("redirect")
	if err == nil && redirect.Value != "" {
		registerURL += "?redirect=" + url.QueryEscape(redirect.Value)
	}
	http.Redirect(w, r, registerURL, http.StatusFound)
	return nil
}

func initMusicManager(r *http.Request) (interface{}, error) {
	// Try to read all multipart files to memory.  This is necessary
	// due to the way tracksInsert works: the HTTP response is
	// returned when the upload starts, not when it finishes.
	// The upload is thus done in a goroutine that outlives the
	// request handler, and any temporary files it could create.
	//
	// We substract one from MaxInt64 because the code for
	// ParseMultipartForm adds one to the argument for whatever
	// reason.
	err := r.ParseMultipartForm(math.MaxInt64 - 1)
	if err != nil && err != http.ErrNotMultipart {
		return nil, err
	}
	// If either the access token or uploader ID cookie is missing,
	// autoredirect to the start of the authorization flow rather
	// than just report an error.  The redirect parameter lets the
	// user continue right where they left off.

	// BUG(lor): If the access_token cookie expires just before
	// submitting new tracks for upload, they will need to be
	// resubmitted after the auth flow has finished, as it cannot
	// preserve POST data.
	tok, _ := r.Cookie("access_token")
	id, _ := r.Cookie("uploader_id")
	if tok == nil || id == nil {
		path := url.QueryEscape(r.URL.Path + "?" + r.URL.RawQuery)
		return nil, &RESTError{
			Code:     http.StatusFound,
			Message:  "missing credentials",
			Location: "/auth?redirect=" + path,
		}
	}
	// Create and return a new Music Manager service.  On App
	// Engine, fixTransport turns off SSL verification for the
	// transport so that access to the android.clients.google.com
	// server works fine.
	c := getContext(r)
	client := conf.Client(c, &oauth2.Token{AccessToken: tok.Value})
	fixTransport(client.Transport.(*oauth2.Transport).Base)
	return musicmanager.New(client, id.Value)
}

func register(client interface{}, w http.ResponseWriter, r *http.Request) error {
	name := r.FormValue("name")
	if name == "" {
		name = "Google Play Music Web Manager"
	}
	err := client.(*musicmanager.Service).Register(name).Do()
	if err != nil {
		return err
	}
	if redirect := r.FormValue("redirect"); redirect != "" {
		http.Redirect(w, r, redirect, http.StatusFound)
	}
	fmt.Fprintln(w, musicmanager.GetRegisterError("OK"))
	return err
}

func tracksGet(client interface{}, w http.ResponseWriter, r *http.Request) error {
	ID := r.URL.Path
	track, err := client.(*musicmanager.Service).Tracks.Get(ID).Do()
	if err != nil {
		return err
	}
	if name := track.Name(); name != "" {
		// url.QueryEscape encodes spaces as plus signs, which
		// popular browsers don't understand.  Percent-encode
		// them instead.
		name = url.QueryEscape(name)
		if tmp, err := regexpReplaceAllString(`\+`, name, "%20"); err == nil {
			name = tmp
		}
		name = "attachment; filename*=UTF8-''" + name
		w.Header().Set("Content-Disposition", name)
	}
	if size := track.Size(); size > 0 {
		size := strconv.FormatInt(size, 10)
		w.Header().Set("Content-Length", size)
	}
	w.Header().Set("Content-Type", "audio/mpeg")
	_, err = io.Copy(w, track)
	return err
}

func tracksList(client interface{}, w http.ResponseWriter, r *http.Request) error {
	list := client.(*musicmanager.Service).Tracks.List()
	// Parse the options.
	//if tmp := list.UpdatedMin(r.FormValue("updatedMin")); tmp != nil {
	//	list = tmp
	//}
	purchasedOnly, err := strconv.ParseBool(r.FormValue("purchasedOnly"))
	if err == nil {
		list.PurchasedOnly(purchasedOnly)
	}
	if pageToken := r.FormValue("pageToken"); pageToken != "" {
		list.PageToken(pageToken)
	}
	// Execute the query.
	res, err := list.Do()
	if err != nil {
		return err
	}
	// Print the results.
	// GetTracksToExportResponse doesn't report back whether it
	// was obtained with *ExportType == ALL or
	// *ExportType == PURCHASED_AND_PROMOTIONAL, and I don't want
	// to define a new type just to pass this information to the
	// template.  Fortunately, the Go protobuf compiler includes
	// an XXX_unrecognized []byte field with every struct, which
	// is perfect for smuggling this information to the template.
	res.XXX_unrecognized = nil
	if purchasedOnly {
		res.XXX_unrecognized = []byte("true")
	}
	return tpls.ExecuteTemplate(w, "list.tpl", res)
}

func tracksInsert(client interface{}, w http.ResponseWriter, r *http.Request) error {
	f, _, err := r.FormFile("track")
	if err != nil {
		return err
	}
	defer f.Close()
	if err := checkSize(f); err != nil {
		return err
	}
	serverID, err := client.(*musicmanager.Service).Tracks.Insert(f).Do()
	if err != nil {
		return err
	}
	http.Redirect(w, r, "/tracks/", http.StatusFound)
	fmt.Fprintln(w, serverID)
	return nil
}

/*
func jobsList(client interface{}, w http.ResponseWriter, r *http.Request) error {
	jobs, err := client.(*musicmanager.Service).Jobs.List().Do()
	if err != nil {
		return err
	}
	for _, job := range jobs {
		fmt.Fprintf(w, "%+v\n", job)
	}
	return nil
}

func jobsCancel(client interface{}, w http.ResponseWriter, r *http.Request) error {
	err := client.(*musicmanager.Service).Jobs.Cancel().Do()
	if err != nil {
		return err
	}
	http.Redirect(w, r, "/jobs/", http.StatusSeeOther)
	return nil
}
*/
<commit_msg>Minor refactoring<commit_after>package main

import (
	"fmt"
	"html/template"
	"io"
	"net/http"
	"net/url"
	"strconv"

	"golang.org/x/oauth2"
	"google.golang.org/api/plus/v1"

	"google-musicmanager-go"
)

var funcMap = map[string]interface{}{
	"reverse": reversed,
}
var scopes = []string{musicmanager.MusicManagerScope, plus.PlusMeScope}
var conf = googleMustConfigFromFile("credentials.json", scopes...)
var tpls = template.Must(template.New("static").
	Funcs(funcMap).
	ParseGlob("static/*.tpl"))

func init() {
	http.Handle("/static/", http.FileServer(http.Dir(".")))
	http.Handle("/auth", &REST{Get: auth})
	http.Handle("/oauth2callback", &REST{Get: oauth2callback})
	http.Handle("/register", &REST{
		Init: initMusicManager,
		Get:  register,
	})
	http.Handle("/tracks/", &REST{
		Init:   initMusicManager,
		Get:    tracksGet,
		List:   tracksList,
		Insert: tracksInsert,
	})
	/*
		http.Handle("/jobs/", &REST{
			Init:   initMusicManager,
			List:   jobsList,
			Delete: jobsCancel,
		})
	*/
}

func auth(_ interface{}, w http.ResponseWriter, r *http.Request) error {
	state, err := nonce(32)
	if err != nil {
		return err
	}
	// BUG(lor): Google Play Music Web Manager ignores the
	// "redirect_uris" property of the credentials file.  Instead,
	// a redirect URL is always created dynamically by appending
	// "/oauth2callback" to the scheme and host of the domain on
	// which Google Play Music Web Manager received the request for
	// "/auth".  This is so that the redirect URL can be generated
	// correctly on the App Engine dev server.
	conf.RedirectURL = getRedirectURL(getContext(r))
	httpSetCookie(w, r, "state", state)
	httpSetCookie(w, r, "redirect", r.FormValue("redirect"))
	http.Redirect(w, r, conf.AuthCodeURL(state), http.StatusFound)
	return nil
}

func oauth2callback(_ interface{}, w http.ResponseWriter, r *http.Request) error {
	// Confirm that the state matches the nonce we stored
	// (See https://tools.ietf.org/html/rfc6749#section-10.12.)
	rstate := r.FormValue("state")
	astate, err := r.Cookie("state")
	if err != nil || rstate != astate.Value {
		return &RESTError{
			Code: http.StatusBadRequest,
			Message: "state parameter and cookie mismatch" +
				"; have you perhaps disabled cookies?",
		}
	}
	// Exchange the authorization code for an access token.
	c := getContext(r)
	tok, err := conf.Exchange(c, r.FormValue("code"))
	if err != nil {
		return err
	}
	// BUG(lor): Google Play Music does not allow downloading tracks
	// with an uploader_id that is not sufficiently
	// "MAC address-like" (perhaps it only checks for a colon?)
	// The /oauth2callback endpoint generates the uploader_id by
	// injecting a colon between every two digits of the user's
	// Google Account ID, which appears to suffice.
	client := conf.Client(c, tok)
	plus, err := plus.New(client)
	if err != nil {
		return err
	}
	person, err := plus.People.Get("me").Do()
	if err != nil {
		return err
	}
	id, err := regexpReplaceAllString(`(..)`, person.Id, "$1:")
	if err != nil {
		return err
	}
	// Wipe the state and redirect cookies, no longer necessary,
	// and store the access token and uploader ID as cookies.
	httpSetCookie(w, r, "state", "")
	httpSetCookie(w, r, "redirect", "")
	httpSetCookie(w, r, "access_token", tok.AccessToken)
	httpSetCookie(w, r, "uploader_id", id)
	// We autoredirect to the registration endpoint for convenience.
	// If the redirect cookie was provided, remember to tell the
	// registration endpoint to continue there after success.
	registerURL := "/register"
	redirect, err := r.Cookie("redirect")
	if err == nil && redirect.Value != "" {
		registerURL += "?redirect=" + url.QueryEscape(redirect.Value)
	}
	http.Redirect(w, r, registerURL, http.StatusFound)
	return nil
}

func initMusicManager(r *http.Request) (interface{}, error) {
	// If either the access token or uploader ID cookie is missing,
	// autoredirect to the start of the authorization flow rather
	// than just report an error.  The redirect parameter lets the
	// user continue right where they left off.

	// BUG(lor): If the access_token cookie expires just before
	// submitting new tracks for upload, they will need to be
	// resubmitted after the auth flow has finished, as it cannot
	// preserve POST data.
	tok, _ := r.Cookie("access_token")
	id, _ := r.Cookie("uploader_id")
	if tok == nil || id == nil {
		path := url.QueryEscape(r.URL.Path + "?" + r.URL.RawQuery)
		return nil, &RESTError{
			Code:     http.StatusFound,
			Message:  "missing credentials",
			Location: "/auth?redirect=" + path,
		}
	}
	// Create and return a new Music Manager service.  On App
	// Engine, fixTransport turns off SSL verification for the
	// transport so that access to the android.clients.google.com
	// server works fine.
	c := getContext(r)
	client := conf.Client(c, &oauth2.Token{AccessToken: tok.Value})
	fixTransport(client.Transport.(*oauth2.Transport).Base)
	return musicmanager.New(client, id.Value)
}

func register(client interface{}, w http.ResponseWriter, r *http.Request) error {
	name := r.FormValue("name")
	if name == "" {
		name = "Google Play Music Web Manager"
	}
	err := client.(*musicmanager.Service).Register(name).Do()
	if err != nil {
		return err
	}
	if redirect := r.FormValue("redirect"); redirect != "" {
		http.Redirect(w, r, redirect, http.StatusFound)
	}
	fmt.Fprintln(w, musicmanager.GetRegisterError("OK"))
	return err
}

func tracksGet(client interface{}, w http.ResponseWriter, r *http.Request) error {
	id := r.URL.Path
	track, err := client.(*musicmanager.Service).Tracks.Get(id).Do()
	if err != nil {
		return err
	}
	if name := track.Name(); name != "" {
		name = url.QueryEscape(name)
		// url.QueryEscape encodes spaces as plus signs, which
		// popular browsers don't understand.  Try to
		// percent-encode them instead.
		if tmp, err := regexpReplaceAllString(`\+`, name, "%20"); err == nil {
			name = tmp
		}
		name = "attachment; filename*=UTF8-''" + name
		w.Header().Set("Content-Disposition", name)
	}
	if size := track.Size(); size > 0 {
		size := strconv.FormatInt(size, 10)
		w.Header().Set("Content-Length", size)
	}
	w.Header().Set("Content-Type", "audio/mpeg")
	_, err = io.Copy(w, track)
	return err
}

func tracksList(client interface{}, w http.ResponseWriter, r *http.Request) error {
	list := client.(*musicmanager.Service).Tracks.List()
	// Parse the options.
	//if tmp := list.UpdatedMin(r.FormValue("updatedMin")); tmp != nil {
	//	list = tmp
	//}
	purchasedOnly, err := strconv.ParseBool(r.FormValue("purchasedOnly"))
	if err == nil {
		list.PurchasedOnly(purchasedOnly)
	}
	if pageToken := r.FormValue("pageToken"); pageToken != "" {
		list.PageToken(pageToken)
	}
	// Execute the query.
	res, err := list.Do()
	if err != nil {
		return err
	}
	// Print the results.
	// GetTracksToExportResponse doesn't report back whether it
	// was obtained with *ExportType == ALL or
	// *ExportType == PURCHASED_AND_PROMOTIONAL, and I don't want
	// to define a new type just to pass this information to the
	// template.  Fortunately, the Go protobuf compiler includes
	// an XXX_unrecognized []byte field with every struct, which
	// is perfect for smuggling this information to the template.
	res.XXX_unrecognized = nil
	if purchasedOnly {
		res.XXX_unrecognized = []byte("true")
	}
	return tpls.ExecuteTemplate(w, "list.tpl", res)
}

func tracksInsert(client interface{}, w http.ResponseWriter, r *http.Request) error {
	f, _, err := r.FormFile("track")
	if err != nil {
		return err
	}
	defer f.Close()
	if err := checkSize(f); err != nil {
		return err
	}
	serverID, err := client.(*musicmanager.Service).Tracks.Insert(f).Do()
	if err != nil {
		return err
	}
	http.Redirect(w, r, "/tracks/", http.StatusFound)
	fmt.Fprintln(w, serverID)
	return nil
}

/*
func jobsList(client interface{}, w http.ResponseWriter, r *http.Request) error {
	jobs, err := client.(*musicmanager.Service).Jobs.List().Do()
	if err != nil {
		return err
	}
	for _, job := range jobs {
		fmt.Fprintf(w, "%+v\n", job)
	}
	return nil
}

func jobsCancel(client interface{}, w http.ResponseWriter, r *http.Request) error {
	err := client.(*musicmanager.Service).Jobs.Cancel().Do()
	if err != nil {
		return err
	}
	http.Redirect(w, r, "/jobs/", http.StatusSeeOther)
	return nil
}
*/
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"strings"
)

func hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello world!")
}

func main() {
	http.HandleFunc("/", hello)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Use logrus<commit_after>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"net/http"
	"strings"
)

func hello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello world!")
}

func main() {
	http.HandleFunc("/", hello)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 CUE Authors
//
// 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 kubernetes

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"testing"

	"github.com/kylelemons/godebug/diff"

	"cuelang.org/go/cmd/cue/cmd"
	"cuelang.org/go/cue/load"
	"cuelang.org/go/internal/copy"
	"cuelang.org/go/internal/cuetest"
)

var (
	cleanup = flag.Bool("cleanup", true, "clean up generated files")
)

func TestTutorial(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	cwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	// Read the tutorial.
	b, err := ioutil.ReadFile("README.md")
	if err != nil {
		t.Fatal(err)
	}

	// Copy test data and change the cwd to this directory.
	dir, err := ioutil.TempDir("", "tutorial")
	if err != nil {
		log.Fatal(err)
	}
	if *cleanup {
		defer os.RemoveAll(dir)
	} else {
		defer logf(t, "Temporary dir: %v", dir)
	}

	wd := filepath.Join(dir, "services")
	if err := copy.Dir(filepath.Join("original", "services"), wd); err != nil {
		t.Fatal(err)
	}

	run(t, dir, "cue mod init", &config{
		// Stdin: strings.NewReader(input),
	})

	if cuetest.UpdateGoldenFiles {
		// The test environment won't work in all environments. We create
		// a fake go.mod so that Go will find the module root. By default
		// we won't set it.
		if err := os.Chdir(dir); err != nil {
			t.Fatal(err)
		}
		cmd := exec.Command("go", "mod", "init", "cuelang.org/dummy")
		b, err := cmd.CombinedOutput()
		logf(t, string(b))
		if err != nil {
			t.Fatal(err)
		}
	} else {
		// We only fetch new kubernetes files with when updating.
		err := copy.Dir(load.GenPath("quick"), load.GenPath(dir))
		if err != nil {
			t.Fatal(err)
		}
	}

	if err := os.Chdir(wd); err != nil {
		t.Fatal(err)
	}
	defer os.Chdir(cwd)
	logf(t, "Changed to directory: %s", wd)

	// Execute the tutorial.
	for c := cuetest.NewChunker(t, b); c.Next("```", "```"); {
		for c := cuetest.NewChunker(t, c.Bytes()); c.Next("$ ", "\n"); {
			alt := c.Text()
			cmd := strings.Replace(alt, "<<EOF", "", -1)

			input := ""
			if cmd != alt {
				if !c.Next("", "EOF") {
					t.Fatalf("non-terminated <<EOF")
				}
				input = c.Text()
			}

			redirect := ""
			if p := strings.Index(cmd, " >"); p > 0 {
				redirect = cmd[p+1:]
				cmd = cmd[:p]
			}

			logf(t, "$ %s", cmd)
			switch cmd = strings.TrimSpace(cmd); {
			case strings.HasPrefix(cmd, "cat"):
				if input == "" {
					break
				}
				var r *os.File
				var err error
				if strings.HasPrefix(redirect, ">>") {
					// Append input
					r, err = os.OpenFile(
						strings.TrimSpace(redirect[2:]),
						os.O_APPEND|os.O_CREATE|os.O_WRONLY,
						0666)
				} else { // strings.HasPrefix(redirect, ">")
					// Create new file with input
					r, err = os.Create(strings.TrimSpace(redirect[1:]))
				}
				if err != nil {
					t.Fatal(err)
				}
				_, err = io.WriteString(r, input)
				if err := r.Close(); err != nil {
					t.Fatal(err)
				}
				if err != nil {
					t.Fatal(err)
				}

			case strings.HasPrefix(cmd, "cue "):
				if strings.HasPrefix(cmd, "cue create") {
					// Don't execute the kubernetes dry run.
					break
				}
				if strings.HasPrefix(cmd, "cue mod init") {
					// Already ran this at setup.
					break
				}

				if !cuetest.UpdateGoldenFiles && strings.HasPrefix(cmd, "cue get") {
					// Don't fetch stuff in normal mode.
					break
				}

				run(t, wd, cmd, &config{
					Stdin:  strings.NewReader(input),
					Stdout: os.Stdout,
				})

			case strings.HasPrefix(cmd, "sed "):
				c := cuetest.NewChunker(t, []byte(cmd))
				c.Next("s/", "/")
				re := regexp.MustCompile(c.Text())
				c.Next("", "/'")
				repl := c.Bytes()
				c.Next(" ", ".cue")
				file := c.Text() + ".cue"
				b, err := ioutil.ReadFile(file)
				if err != nil {
					t.Fatal(err)
				}
				b = re.ReplaceAll(b, repl)
				err = ioutil.WriteFile(file, b, 0644)
				if err != nil {
					t.Fatal(err)
				}

			case strings.HasPrefix(cmd, "touch "):
				logf(t, "$ %s", cmd)
				file := strings.TrimSpace(cmd[len("touch "):])
				err := ioutil.WriteFile(file, []byte(""), 0644)
				if err != nil {
					t.Fatal(err)
				}
			}
		}
	}

	if err := os.Chdir(filepath.Join(cwd, "quick")); err != nil {
		t.Fatal(err)
	}

	if cuetest.UpdateGoldenFiles {
		// Remove all old cue files.
		err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
			if isCUE(path) {
				if err := os.Remove(path); err != nil {
					t.Fatal(err)
				}
			}
			return err
		})
		if err != nil {
			t.Fatal(err)
		}

		err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
			if isCUE(path) {
				dst := path[len(dir)+1:]
				err := os.MkdirAll(filepath.Dir(dst), 0755)
				if err != nil {
					return err
				}
				return copy.File(path, dst)
			}
			return err
		})
		if err != nil {
			t.Fatal(err)
		}
		return
	}

	// Compare the output in the temp directory with the quick output.
	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if filepath.Ext(path) != ".cue" {
			return nil
		}
		b1, err := ioutil.ReadFile(path)
		if err != nil {
			t.Fatal(err)
		}
		b2, err := ioutil.ReadFile(path[len(dir)+1:])
		if err != nil {
			t.Fatal(err)
		}
		got, want := string(b1), string(b2)
		if got != want {
			t.Log(diff.Diff(got, want))
			return fmt.Errorf("file %q differs", path)
		}
		return nil
	})
	if err != nil {
		t.Error(err)
	}
}

func isCUE(filename string) bool {
	return filepath.Ext(filename) == ".cue" && !strings.Contains(filename, "_tool")
}

func TestEval(t *testing.T) {
	for _, dir := range []string{"quick", "manual"} {
		t.Run(dir, func(t *testing.T) {
			buf := &bytes.Buffer{}
			run(t, dir, "cue eval ./...", &config{
				Stdout: buf,
			})

			cwd, _ := os.Getwd()
			pattern := fmt.Sprintf("//.*%s.*", regexp.QuoteMeta(filepath.Join(cwd, dir)))
			re, err := regexp.Compile(pattern)
			if err != nil {
				t.Fatal(err)
			}
			got := re.ReplaceAll(buf.Bytes(), []byte{})
			got = bytes.TrimSpace(got)

			testfile := filepath.Join("testdata", dir+".out")

			if cuetest.UpdateGoldenFiles {
				err := ioutil.WriteFile(testfile, got, 0644)
				if err != nil {
					t.Fatal(err)
				}
				return
			}

			b, err := ioutil.ReadFile(testfile)
			if err != nil {
				t.Fatal(err)
			}

			if got, want := string(got), string(b); got != want {
				t.Log(got)
				t.Errorf("output differs for file %s in %s", testfile, cwd)
			}
		})
	}
}

type config struct {
	Stdin  io.Reader
	Stdout io.Writer
	Golden string
}

// run executes the given command in the given directory and reports any
// errors comparing it to the gold standard.
func run(t *testing.T, dir, command string, cfg *config) {
	if cfg == nil {
		cfg = &config{}
	}

	old, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	if err = os.Chdir(dir); err != nil {
		t.Fatal(err)
	}
	defer func() { os.Chdir(old) }()

	logf(t, "Executing command: %s", command)

	command = strings.TrimSpace(command[4:])
	args := splitArgs(t, command)
	logf(t, "Args: %q", args)

	buf := &bytes.Buffer{}
	if cfg.Golden != "" {
		if cfg.Stdout != nil {
			t.Fatal("cannot set Golden and Stdout")
		}
		cfg.Stdout = buf
	}
	cmd, err := cmd.New(args)
	if err != nil {
		t.Fatal(err)
	}
	if cfg.Stdout != nil {
		cmd.SetOutput(cfg.Stdout)
	} else {
		cmd.SetOutput(buf)
	}
	if cfg.Stdin != nil {
		cmd.SetInput(cfg.Stdin)
	}
	if err = cmd.Run(context.Background()); err != nil {
		if cfg.Stdout == nil {
			logf(t, "Output:\n%s", buf.String())
		}
		logf(t, "Execution failed: %v", err)
	}

	if cfg.Golden == "" {
		return
	}

	pattern := fmt.Sprintf("//.*%s.*", regexp.QuoteMeta(dir))
	re, err := regexp.Compile(pattern)
	if err != nil {
		t.Fatal(err)
	}
	got := re.ReplaceAllString(buf.String(), "")
	got = strings.TrimSpace(got)

	want := strings.TrimSpace(cfg.Golden)
	if got != want {
		t.Errorf("files differ:\n%s", diff.Diff(got, want))
	}
}

func logf(t *testing.T, format string, args ...interface{}) {
	t.Helper()
	t.Logf(format, args...)
}

func splitArgs(t *testing.T, s string) (args []string) {
	c := cuetest.NewChunker(t, []byte(s))
	for {
		found := c.Find(" '")
		args = append(args, strings.Split(c.Text(), " ")...)
		if !found {
			break
		}
		c.Next("", "' ")
		args = append(args, c.Text())
	}
	return args
}
<commit_msg>doc: fix running Kubernetes test with CUE_UPDATE=1<commit_after>// Copyright 2019 CUE Authors
//
// 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 kubernetes

import (
	"bytes"
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"testing"

	"github.com/kylelemons/godebug/diff"

	"cuelang.org/go/cmd/cue/cmd"
	"cuelang.org/go/cue/load"
	"cuelang.org/go/internal/copy"
	"cuelang.org/go/internal/cuetest"
)

var (
	cleanup = flag.Bool("cleanup", true, "clean up generated files")
)

func TestTutorial(t *testing.T) {
	if testing.Short() {
		t.Skip()
	}

	cwd, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}

	// Read the tutorial.
	b, err := ioutil.ReadFile("README.md")
	if err != nil {
		t.Fatal(err)
	}

	// Copy test data and change the cwd to this directory.
	dir, err := ioutil.TempDir("", "tutorial")
	if err != nil {
		log.Fatal(err)
	}
	if *cleanup {
		defer os.RemoveAll(dir)
	} else {
		defer logf(t, "Temporary dir: %v", dir)
	}

	wd := filepath.Join(dir, "services")
	if err := copy.Dir(filepath.Join("original", "services"), wd); err != nil {
		t.Fatal(err)
	}

	run(t, dir, "cue mod init", &config{
		// Stdin: strings.NewReader(input),
	})

	if cuetest.UpdateGoldenFiles {
		// The test environment won't work in all environments. We create
		// a fake go.mod so that Go will find the module root. By default
		// we won't set it.
		out := execute(t, dir, "go", "mod", "init", "cuelang.org/dummy")
		logf(t, "%s", out)
	} else {
		// We only fetch new kubernetes files with when updating.
		err := copy.Dir(load.GenPath("quick"), load.GenPath(dir))
		if err != nil {
			t.Fatal(err)
		}
	}

	if err := os.Chdir(wd); err != nil {
		t.Fatal(err)
	}
	defer os.Chdir(cwd)
	logf(t, "Changed to directory: %s", wd)

	// Execute the tutorial.
	for c := cuetest.NewChunker(t, b); c.Next("```", "```"); {
		for c := cuetest.NewChunker(t, c.Bytes()); c.Next("$ ", "\n"); {
			alt := c.Text()
			cmd := strings.Replace(alt, "<<EOF", "", -1)

			input := ""
			if cmd != alt {
				if !c.Next("", "EOF") {
					t.Fatalf("non-terminated <<EOF")
				}
				input = c.Text()
			}

			redirect := ""
			if p := strings.Index(cmd, " >"); p > 0 {
				redirect = cmd[p+1:]
				cmd = cmd[:p]
			}

			logf(t, "$ %s", cmd)
			switch cmd = strings.TrimSpace(cmd); {
			case strings.HasPrefix(cmd, "cat"):
				if input == "" {
					break
				}
				var r *os.File
				var err error
				if strings.HasPrefix(redirect, ">>") {
					// Append input
					r, err = os.OpenFile(
						strings.TrimSpace(redirect[2:]),
						os.O_APPEND|os.O_CREATE|os.O_WRONLY,
						0666)
				} else { // strings.HasPrefix(redirect, ">")
					// Create new file with input
					r, err = os.Create(strings.TrimSpace(redirect[1:]))
				}
				if err != nil {
					t.Fatal(err)
				}
				_, err = io.WriteString(r, input)
				if err := r.Close(); err != nil {
					t.Fatal(err)
				}
				if err != nil {
					t.Fatal(err)
				}

			case strings.HasPrefix(cmd, "cue "):
				if strings.HasPrefix(cmd, "cue create") {
					// Don't execute the kubernetes dry run.
					break
				}
				if strings.HasPrefix(cmd, "cue mod init") {
					// Already ran this at setup.
					break
				}

				if !cuetest.UpdateGoldenFiles && strings.HasPrefix(cmd, "cue get") {
					// Don't fetch stuff in normal mode.
					break
				}

				run(t, wd, cmd, &config{
					Stdin:  strings.NewReader(input),
					Stdout: os.Stdout,
				})

			case strings.HasPrefix(cmd, "sed "):
				c := cuetest.NewChunker(t, []byte(cmd))
				c.Next("s/", "/")
				re := regexp.MustCompile(c.Text())
				c.Next("", "/'")
				repl := c.Bytes()
				c.Next(" ", ".cue")
				file := c.Text() + ".cue"
				b, err := ioutil.ReadFile(file)
				if err != nil {
					t.Fatal(err)
				}
				b = re.ReplaceAll(b, repl)
				err = ioutil.WriteFile(file, b, 0644)
				if err != nil {
					t.Fatal(err)
				}

			case strings.HasPrefix(cmd, "touch "):
				logf(t, "$ %s", cmd)
				file := strings.TrimSpace(cmd[len("touch "):])
				err := ioutil.WriteFile(file, []byte(""), 0644)
				if err != nil {
					t.Fatal(err)
				}
			case strings.HasPrefix(cmd, "go "):
				if !cuetest.UpdateGoldenFiles && strings.HasPrefix(cmd, "go get") {
					// Don't fetch stuff in normal mode.
					break
				}

				out := execute(t, wd, splitArgs(t, cmd)...)
				logf(t, "%s", out)
			}
		}
	}

	if err := os.Chdir(filepath.Join(cwd, "quick")); err != nil {
		t.Fatal(err)
	}

	if cuetest.UpdateGoldenFiles {
		// Remove all old cue files.
		err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
			if isCUE(path) {
				if err := os.Remove(path); err != nil {
					t.Fatal(err)
				}
			}
			return err
		})
		if err != nil {
			t.Fatal(err)
		}

		err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
			if isCUE(path) {
				dst := path[len(dir)+1:]
				err := os.MkdirAll(filepath.Dir(dst), 0755)
				if err != nil {
					return err
				}
				return copy.File(path, dst)
			}
			return err
		})
		if err != nil {
			t.Fatal(err)
		}
		return
	}

	// Compare the output in the temp directory with the quick output.
	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if filepath.Ext(path) != ".cue" {
			return nil
		}
		b1, err := ioutil.ReadFile(path)
		if err != nil {
			t.Fatal(err)
		}
		b2, err := ioutil.ReadFile(path[len(dir)+1:])
		if err != nil {
			t.Fatal(err)
		}
		got, want := string(b1), string(b2)
		if got != want {
			t.Log(diff.Diff(got, want))
			return fmt.Errorf("file %q differs", path)
		}
		return nil
	})
	if err != nil {
		t.Error(err)
	}
}

func isCUE(filename string) bool {
	return filepath.Ext(filename) == ".cue" && !strings.Contains(filename, "_tool")
}

func TestEval(t *testing.T) {
	for _, dir := range []string{"quick", "manual"} {
		t.Run(dir, func(t *testing.T) {
			buf := &bytes.Buffer{}
			run(t, dir, "cue eval ./...", &config{
				Stdout: buf,
			})

			cwd, _ := os.Getwd()
			pattern := fmt.Sprintf("//.*%s.*", regexp.QuoteMeta(filepath.Join(cwd, dir)))
			re, err := regexp.Compile(pattern)
			if err != nil {
				t.Fatal(err)
			}
			got := re.ReplaceAll(buf.Bytes(), []byte{})
			got = bytes.TrimSpace(got)

			testfile := filepath.Join("testdata", dir+".out")

			if cuetest.UpdateGoldenFiles {
				err := ioutil.WriteFile(testfile, got, 0644)
				if err != nil {
					t.Fatal(err)
				}
				return
			}

			b, err := ioutil.ReadFile(testfile)
			if err != nil {
				t.Fatal(err)
			}

			if got, want := string(got), string(b); got != want {
				t.Log(got)
				t.Errorf("output differs for file %s in %s", testfile, cwd)
			}
		})
	}
}

type config struct {
	Stdin  io.Reader
	Stdout io.Writer
	Golden string
}

// execute executes the given command in the given directory
func execute(t *testing.T, dir string, args ...string) string {
	old, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	if err = os.Chdir(dir); err != nil {
		t.Fatal(err)
	}
	defer func() { os.Chdir(old) }()

	logf(t, "Executing command: %s", strings.Join(args, " "))

	cmd := exec.Command(args[0], args[1:]...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("failed to run [%v] in %s: %v\n%s", cmd, dir, err, out)
	}
	return string(out)
}

// run executes the given command in the given directory and reports any
// errors comparing it to the gold standard.
func run(t *testing.T, dir, command string, cfg *config) {
	if cfg == nil {
		cfg = &config{}
	}

	old, err := os.Getwd()
	if err != nil {
		t.Fatal(err)
	}
	if err = os.Chdir(dir); err != nil {
		t.Fatal(err)
	}
	defer func() { os.Chdir(old) }()

	logf(t, "Executing command: %s", command)

	command = strings.TrimSpace(command[4:])
	args := splitArgs(t, command)
	logf(t, "Args: %q", args)

	buf := &bytes.Buffer{}
	if cfg.Golden != "" {
		if cfg.Stdout != nil {
			t.Fatal("cannot set Golden and Stdout")
		}
		cfg.Stdout = buf
	}
	cmd, err := cmd.New(args)
	if err != nil {
		t.Fatal(err)
	}
	if cfg.Stdout != nil {
		cmd.SetOutput(cfg.Stdout)
	} else {
		cmd.SetOutput(buf)
	}
	if cfg.Stdin != nil {
		cmd.SetInput(cfg.Stdin)
	}
	if err = cmd.Run(context.Background()); err != nil {
		if cfg.Stdout == nil {
			logf(t, "Output:\n%s", buf.String())
		}
		logf(t, "Execution failed: %v", err)
	}

	if cfg.Golden == "" {
		return
	}

	pattern := fmt.Sprintf("//.*%s.*", regexp.QuoteMeta(dir))
	re, err := regexp.Compile(pattern)
	if err != nil {
		t.Fatal(err)
	}
	got := re.ReplaceAllString(buf.String(), "")
	got = strings.TrimSpace(got)

	want := strings.TrimSpace(cfg.Golden)
	if got != want {
		t.Errorf("files differ:\n%s", diff.Diff(got, want))
	}
}

func logf(t *testing.T, format string, args ...interface{}) {
	t.Helper()
	t.Logf(format, args...)
}

func splitArgs(t *testing.T, s string) (args []string) {
	c := cuetest.NewChunker(t, []byte(s))
	for {
		found := c.Find(" '")
		args = append(args, strings.Split(c.Text(), " ")...)
		if !found {
			break
		}
		c.Next("", "' ")
		args = append(args, c.Text())
	}
	return args
}
<|endoftext|>


<commit_before>package disruptor

import "errors"

type Wireup struct {
	spinWait       bool
	waiter         WaitStrategy
	capacity       int64
	consumerGroups [][]Consumer
}
type Option func(*Wireup)

func New(options ...Option) (*Wireup, error) {
	this := &Wireup{}

	WithSpinWait(true)(this)
	WithWaitStrategy(NewWaitStrategy())(this)

	for _, option := range options {
		option(this)
	}

	if err := this.validate(); err != nil {
		return nil, err
	}

	return this, nil
}
func (this *Wireup) validate() error {
	if this.waiter == nil {
		return errMissingWaitStrategy
	}

	if this.capacity <= 0 {
		return errCapacityTooSmall
	}

	if this.capacity&(this.capacity-1) != 0 {
		return errCapacityPowerOfTwo
	}

	if len(this.consumerGroups) == 0 {
		return errMissingConsumers
	}

	for _, consumerGroup := range this.consumerGroups {
		if len(consumerGroup) == 0 {
			return errMissingConsumersInGroup
		}

		for _, consumer := range consumerGroup {
			if consumer == nil {
				return errEmptyConsumer
			}
		}
	}

	return nil
}

func (this *Wireup) Build() (Sequencer, ListenCloser) {
	var writerSequence = NewSequence()
	listeners, listenBarrier := this.buildListeners(writerSequence)
	return this.buildSequencer(writerSequence, listenBarrier), compositeListener(listeners)
}
func (this *Wireup) buildListeners(writerSequence *Sequence) (listeners []ListenCloser, upstream Barrier) {
	upstream = writerSequence

	for _, consumerGroup := range this.consumerGroups {
		var consumerGroupSequences []*Sequence

		for _, consumer := range consumerGroup {
			currentSequence := NewSequence()
			listeners = append(listeners, NewListener(currentSequence, writerSequence, upstream, this.waiter, consumer))
			consumerGroupSequences = append(consumerGroupSequences, currentSequence)
		}

		upstream = NewCompositeBarrier(consumerGroupSequences...)
	}

	return listeners, upstream
}
func (this *Wireup) buildSequencer(writerSequence *Sequence, readBarrier Barrier) Sequencer {
	var sequencer Sequencer = NewSequencer(writerSequence, readBarrier, this.capacity)
	if this.spinWait {
		return NewSpinSequencer(sequencer)
	}

	return sequencer
}

func WithSpinWait(value bool) Option             { return func(this *Wireup) { this.spinWait = value } }
func WithWaitStrategy(value WaitStrategy) Option { return func(this *Wireup) { this.waiter = value } }
func WithCapacity(value int64) Option            { return func(this *Wireup) { this.capacity = value } }
func WithConsumerGroup(value ...Consumer) Option {
	return func(this *Wireup) { this.consumerGroups = append(this.consumerGroups, value) }
}

var (
	errMissingWaitStrategy     = errors.New("a wait strategy must be provided")
	errCapacityTooSmall        = errors.New("the capacity must be at least 1")
	errCapacityPowerOfTwo      = errors.New("the capacity be a power of two, e.g. 2, 4, 8, 16")
	errMissingConsumers        = errors.New("no consumers have been provided")
	errMissingConsumersInGroup = errors.New("the consumer group does not have any consumers")
	errEmptyConsumer           = errors.New("an empty consumer was specified in the consumer group")
)
<commit_msg>New wireup option.<commit_after>package disruptor

import "errors"

type Wireup struct {
	spinWait       bool
	waiter         WaitStrategy
	capacity       int64
	consumerGroups [][]Consumer
}
type Option func(*Wireup)

func RequireNew(options ...Option) *Wireup {
	if this, err := New(options...); err != nil {
		panic(err)
	} else {
		return this
	}
}
func New(options ...Option) (*Wireup, error) {
	this := &Wireup{}

	WithSpinWait(true)(this)
	WithWaitStrategy(NewWaitStrategy())(this)

	for _, option := range options {
		option(this)
	}

	if err := this.validate(); err != nil {
		return nil, err
	}

	return this, nil
}
func (this *Wireup) validate() error {
	if this.waiter == nil {
		return errMissingWaitStrategy
	}

	if this.capacity <= 0 {
		return errCapacityTooSmall
	}

	if this.capacity&(this.capacity-1) != 0 {
		return errCapacityPowerOfTwo
	}

	if len(this.consumerGroups) == 0 {
		return errMissingConsumers
	}

	for _, consumerGroup := range this.consumerGroups {
		if len(consumerGroup) == 0 {
			return errMissingConsumersInGroup
		}

		for _, consumer := range consumerGroup {
			if consumer == nil {
				return errEmptyConsumer
			}
		}
	}

	return nil
}

func (this *Wireup) Build() (Sequencer, ListenCloser) {
	var writerSequence = NewSequence()
	listeners, listenBarrier := this.buildListeners(writerSequence)
	return this.buildSequencer(writerSequence, listenBarrier), compositeListener(listeners)
}
func (this *Wireup) buildListeners(writerSequence *Sequence) (listeners []ListenCloser, upstream Barrier) {
	upstream = writerSequence

	for _, consumerGroup := range this.consumerGroups {
		var consumerGroupSequences []*Sequence

		for _, consumer := range consumerGroup {
			currentSequence := NewSequence()
			listeners = append(listeners, NewListener(currentSequence, writerSequence, upstream, this.waiter, consumer))
			consumerGroupSequences = append(consumerGroupSequences, currentSequence)
		}

		upstream = NewCompositeBarrier(consumerGroupSequences...)
	}

	return listeners, upstream
}
func (this *Wireup) buildSequencer(writerSequence *Sequence, readBarrier Barrier) Sequencer {
	var sequencer Sequencer = NewSequencer(writerSequence, readBarrier, this.capacity)
	if this.spinWait {
		return NewSpinSequencer(sequencer)
	}

	return sequencer
}

func WithSpinWait(value bool) Option             { return func(this *Wireup) { this.spinWait = value } }
func WithWaitStrategy(value WaitStrategy) Option { return func(this *Wireup) { this.waiter = value } }
func WithCapacity(value int64) Option            { return func(this *Wireup) { this.capacity = value } }
func WithConsumerGroup(value ...Consumer) Option {
	return func(this *Wireup) { this.consumerGroups = append(this.consumerGroups, value) }
}

var (
	errMissingWaitStrategy     = errors.New("a wait strategy must be provided")
	errCapacityTooSmall        = errors.New("the capacity must be at least 1")
	errCapacityPowerOfTwo      = errors.New("the capacity be a power of two, e.g. 2, 4, 8, 16")
	errMissingConsumers        = errors.New("no consumers have been provided")
	errMissingConsumersInGroup = errors.New("the consumer group does not have any consumers")
	errEmptyConsumer           = errors.New("an empty consumer was specified in the consumer group")
)
<|endoftext|>