<commit_before>// Copyright (c) 2013, 2014 The btcsuite developers
// Copyright (c) 2017 BitGo
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package provautil_test

import (
	"fmt"
	"github.com/bitgo/prova/btcec"
	"github.com/bitgo/prova/chaincfg"
	"github.com/bitgo/prova/provautil"
	"github.com/btcsuite/golangcrypto/ripemd160"
	"testing"
)

func TestAddresses(t *testing.T) {
	tests := []struct {
		addr   string
		keyIDs []btcec.KeyID
		name   string
		net    *chaincfg.Params
		pkHash []byte
		valid  bool
	}{
		{
			addr:   "G9n66A3tweNBdnrWHtPQhojDvgtTHpKp5Ke5EeHqZM1pv",
			keyIDs: []btcec.KeyID{1, 2},
			name:   "mainnet standard address",
			net:    &chaincfg.MainNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  true,
		},
		{
			addr:   "CBmenNb1jH2fkDXKuEdqUgj3BaLqnGaGAAn6qMQXVv1KzyG8inv6UHMM",
			keyIDs: []btcec.KeyID{1, 2, 3, 4},
			name:   "mainnet 4 of 5 address",
			net:    &chaincfg.MainNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  true,
		},
		{
			addr:   "T9GooXEi927U4tuUkHsyfxtuDwAGFP2RaDXNGVNchBSz3",
			keyIDs: []btcec.KeyID{1, 2},
			name:   "testnet standard address",
			net:    &chaincfg.TestNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  true,
		},
		{
			addr:   "9kT6DCNXS7BcCexSzVMXJ8aZXYxjB4ZGhBqGEU4DKm3vdyZDPbFDPV32np61p76X2Hjepz412YLpoNzwyBuWAWTqsq75ApLf6VaM8zApsumNDXnLLBqR9ZoYDYJgqQU4w2kFMP1e1A",
			keyIDs: []btcec.KeyID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
			name:   "large number of key ids",
			net:    &chaincfg.MainNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  true,
		},
		{
			addr:   "4zia3uJBAsmyZhhjNanqsNPThfGJ6R6BwYuJ6fKc",
			keyIDs: []btcec.KeyID{1},
			name:   "only one key id",
			net:    &chaincfg.TestNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  false,
		},
		{
			addr:   "zFaDkPWYXYgRUvMPFSeE9N7Eu1Hi3WrECgzJLQ1Ye4f1T2dZKgktB3V1U2sqgWYgoMK6Z5RozfmrkwgUxNsnffq21KumgyyJn6rukerMueseUtrgpbU2nKRr3VMHhKRSn5pEACLLKv2eoVz",
			keyIDs: []btcec.KeyID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			name:   "large number of key ids",
			net:    &chaincfg.MainNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  false,
		},
	}

	for _, test := range tests {
		// Decode addr and compare error against valid.
		decoded, err := provautil.DecodeAddress(test.addr, test.net)
		if (err == nil) != test.valid {
			t.Errorf("%v: decoding test failed: %v", test.name, err)
			return
		}

		// Exit early for expected errors.
		if err != nil {
			continue
		}

		// Check that the number of keyids is as expected.
		if len(test.keyIDs) != len(decoded.ScriptKeyIDs()) {
			t.Errorf("%v: keyid counts do not match: %d vs %d", test.name, len(test.keyIDs), len(decoded.ScriptKeyIDs()))
			return
		}

		// Check that the keyids are in the equivalent positions.
		for i, keyID := range decoded.ScriptKeyIDs() {
			if test.keyIDs[i] != keyID {
				t.Errorf("%v: keyid does not match: got %d expected %d", test.name, keyID, test.keyIDs[i])
				return
			}
		}

		// Check the script address (public key hash) length.
		if len(decoded.ScriptAddress()) != ripemd160.Size {
			t.Errorf("%v: pkhash is incorrect size: got %d expected %d", test.name, len(decoded.ScriptAddress()), ripemd160.Size)
			return
		}

		// Check that the pkhash bytes are as expected
		for i, pkHashByte := range decoded.ScriptAddress() {
			if test.pkHash[i] != pkHashByte {
				t.Errorf("%v: pkhash does not match: got %v expected %v", test.name, decoded.ScriptAddress(), test.pkHash)
				return
			}
		}

		// Ensure the stringer returns the same address as the
		// original.
		if decodedStringer, ok := decoded.(fmt.Stringer); ok {
			if test.addr != decodedStringer.String() {
				t.Errorf("%v: String on decoded value does not match expected value: %v != %v",
					test.name, test.addr, decodedStringer.String())
				return
			}
		}

		// Encode again and compare against the original.
		encoded := decoded.EncodeAddress()
		if test.addr != encoded {
			t.Errorf("%v: decoding and encoding produced different addressess: %v != %v",
				test.name, test.addr, encoded)
			return
		}

		// Ensure the address is for the expected network.
		if !decoded.IsForNet(test.net) {
			t.Errorf("%v: calculated network does not match expected",
				test.name)
			return
		}
	}
}
<commit_msg>provautil: Add test coverage to address encoding<commit_after>// Copyright (c) 2013, 2014 The btcsuite developers
// Copyright (c) 2017 BitGo
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package provautil_test

import (
	"fmt"
	"github.com/bitgo/prova/btcec"
	"github.com/bitgo/prova/chaincfg"
	"github.com/bitgo/prova/provautil"
	"github.com/btcsuite/golangcrypto/ripemd160"
	"testing"
)

func TestAddresses(t *testing.T) {
	tests := []struct {
		addr   string
		keyIDs []btcec.KeyID
		name   string
		net    *chaincfg.Params
		pkHash []byte
		valid  bool
	}{
		{
			addr:   "G9n66A3tweNBdnrWHtPQhojDvgtTHpKp5Ke5EeHqZM1pv",
			keyIDs: []btcec.KeyID{1, 2},
			name:   "mainnet standard address",
			net:    &chaincfg.MainNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  true,
		},
		{
			addr:   "CBmenNb1jH2fkDXKuEdqUgj3BaLqnGaGAAn6qMQXVv1KzyG8inv6UHMM",
			keyIDs: []btcec.KeyID{1, 2, 3, 4},
			name:   "mainnet 4 of 5 address",
			net:    &chaincfg.MainNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  true,
		},
		{
			addr:   "T9GooXEi927U4tuUkHsyfxtuDwAGFP2RaDXNGVNchBSz3",
			keyIDs: []btcec.KeyID{1, 2},
			name:   "testnet standard address",
			net:    &chaincfg.TestNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  true,
		},
		{
			addr:   "9kT6DCNXS7BcCexSzVMXJ8aZXYxjB4ZGhBqGEU4DKm3vdyZDPbFDPV32np61p76X2Hjepz412YLpoNzwyBuWAWTqsq75ApLf6VaM8zApsumNDXnLLBqR9ZoYDYJgqQU4w2kFMP1e1A",
			keyIDs: []btcec.KeyID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
			name:   "large number of key ids",
			net:    &chaincfg.MainNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  true,
		},
		{
			addr:   "4zia3uJBAsmyZhhjNanqsNPThfGJ6R6BwYuJ6fKc",
			keyIDs: []btcec.KeyID{1},
			name:   "only one key id",
			net:    &chaincfg.TestNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  false,
		},
		{
			addr:   "zFaDkPWYXYgRUvMPFSeE9N7Eu1Hi3WrECgzJLQ1Ye4f1T2dZKgktB3V1U2sqgWYgoMK6Z5RozfmrkwgUxNsnffq21KumgyyJn6rukerMueseUtrgpbU2nKRr3VMHhKRSn5pEACLLKv2eoVz",
			keyIDs: []btcec.KeyID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			name:   "large number of key ids",
			net:    &chaincfg.MainNetParams,
			pkHash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
			valid:  false,
		},
	}

	for _, test := range tests {
		// Encode the address and check for expected errors
		addr, err := provautil.NewAddressProva(test.pkHash,
			test.keyIDs, test.net)
		if (err == nil) != test.valid {
			t.Errorf("%v: encoding test failed: %v", test.name, err)
		}
		// Exit early for expected errors
		if err != nil {
			continue
		}

		// Compare encoded address against expected
		if test.addr != addr.EncodeAddress() {
			t.Errorf("%v: encoding test failed, got addr %v, expected %v", test.name, addr.EncodeAddress(), test.addr)
		}

		// Decode addr and compare error against valid.
		decoded, err := provautil.DecodeAddress(test.addr, test.net)
		if (err == nil) != test.valid {
			t.Errorf("%v: decoding test failed: %v", test.name, err)
			return
		}

		// Exit early for expected errors.
		if err != nil {
			continue
		}

		// Check that the number of keyids is as expected.
		if len(test.keyIDs) != len(decoded.ScriptKeyIDs()) {
			t.Errorf("%v: keyid counts do not match: %d vs %d", test.name, len(test.keyIDs), len(decoded.ScriptKeyIDs()))
			return
		}

		// Check that the keyids are in the equivalent positions.
		for i, keyID := range decoded.ScriptKeyIDs() {
			if test.keyIDs[i] != keyID {
				t.Errorf("%v: keyid does not match: got %d expected %d", test.name, keyID, test.keyIDs[i])
				return
			}
		}

		// Check the script address (public key hash) length.
		if len(decoded.ScriptAddress()) != ripemd160.Size {
			t.Errorf("%v: pkhash is incorrect size: got %d expected %d", test.name, len(decoded.ScriptAddress()), ripemd160.Size)
			return
		}

		// Check that the pkhash bytes are as expected
		for i, pkHashByte := range decoded.ScriptAddress() {
			if test.pkHash[i] != pkHashByte {
				t.Errorf("%v: pkhash does not match: got %v expected %v", test.name, decoded.ScriptAddress(), test.pkHash)
				return
			}
		}

		// Ensure the stringer returns the same address as the
		// original.
		if decodedStringer, ok := decoded.(fmt.Stringer); ok {
			if test.addr != decodedStringer.String() {
				t.Errorf("%v: String on decoded value does not match expected value: %v != %v",
					test.name, test.addr, decodedStringer.String())
				return
			}
		}

		// Encode again and compare against the original.
		encoded := decoded.EncodeAddress()
		if test.addr != encoded {
			t.Errorf("%v: decoding and encoding produced different addressess: %v != %v",
				test.name, test.addr, encoded)
			return
		}

		// Ensure the address is for the expected network.
		if !decoded.IsForNet(test.net) {
			t.Errorf("%v: calculated network does not match expected",
				test.name)
			return
		}
	}
}
<|endoftext|>


<commit_before>//Copyright 2012, Daniel Morsing
//For licensing information, See the LICENSE file

package main

import (
	"github.com/DanielMorsing/gonzbee/nntp"
	"github.com/DanielMorsing/gonzbee/nzb"
	"github.com/DanielMorsing/gonzbee/yenc"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"
)

type job struct {
	dir string
	n   *nzb.Nzb
}

var downloaderRq = make(chan chan *nntp.Conn)
var downloadReaper = make(chan *nntp.Conn, 1024)

func init() {
	go connectionHandler()
}

func spinUp() *nntp.Conn {
	str := config.GetAddressStr()
	var conn *nntp.Conn
	var err error
	if config.TLS {
		conn, err = nntp.DialTLS(str)
	} else {
		conn, err = nntp.Dial(str)
	}
	if err != nil {
		return nil
	}
	err = conn.Authenticate(config.Username, config.Password)
	if err != nil {
		return nil
	}
	return conn
}

func connectionHandler() {
	var number int
	for {
		ch := <-downloaderRq
		var conn *nntp.Conn
		if number < 10 {
			conn = spinUp()
			if conn == nil {
				continue
			}
			log.Print("Spun up connection #", number+1)
			number++
			ch <- conn
			continue
		}
		conn = <-downloadReaper
		ch <- conn
	}
}

type offsetWriter struct {
	offset int64
	io.WriterAt
}

func (ow *offsetWriter) Write(b []byte) (n int, err error) {
	n, err = ow.WriteAt(b, ow.offset)
	ow.offset += int64(n)
	return
}

func (j *job) handle() {
	var jobDone sync.WaitGroup
	jobDone.Add(len(j.n.File))
	for _, f := range j.n.File {
		var fileinit sync.Once
		var file *os.File
		var fileClose sync.WaitGroup
		fileClose.Add(len(f.Segments))
		go func() {
			fileClose.Wait()
			if file != nil {
				file.Close()
			}
			jobDone.Done()
		}()
		for _, s := range f.Segments {
			ch := make(chan *nntp.Conn)
			downloaderRq <- ch
			conn := <-ch
			go func(seg *nzb.Segment, f *nzb.File) {
				defer fileClose.Done()
				defer func() {
					downloadReaper <- conn
				}()

				err := conn.SwitchGroup(f.Groups[0])
				if err != nil {
					log.Println("Could not switch to group:", err.Error())
					return
				}

				reader, err := conn.GetMessageReader(seg.MsgId)
				if err != nil {
					log.Printf("Could Download MsgId \"%s\": %s\n", seg.MsgId, err.Error())
					return
				}
				defer reader.Close()

				part, err := yenc.NewPart(reader)
				if err != nil {
					log.Printf("Could Decode MsgId \"%s\": %s\n", seg.MsgId, err.Error())
					return
				}

				fileinit.Do(func() {
					file, err = os.Create(filepath.Join(j.dir, part.Filename))
					if err != nil {
						os.Exit(1)
					}
					go func() {
						fileClose.Wait()
						log.Printf("Done downloading file \"%s\"\n", part.Filename)
					}()
				})

				ow := &offsetWriter{
					offset:   part.Begin,
					WriterAt: file,
				}
				_, err = io.Copy(ow, part)
				if err != nil {
					log.Println("Error getting segment:", err.Error())
				}
			}(s, f)
		}
	}
	jobDone.Wait()
}

func mkdir(path string) error {
	fi, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return os.Mkdir(path, 0777)
		}
		return err
	}
	if fi.IsDir() {
		return nil
	} else {
		return os.ErrExist
	}
	panic("unreachable")
}

func jobStart(n *nzb.Nzb, name string, dir string) error {
	workDir := filepath.Join(dir, name)
	err := mkdir(workDir)
	if err != nil {
		return err
	}
	j := &job{
		dir: workDir,
		n:   n,
	}
	j.handle()
	return nil
}
<commit_msg>Simplify and remove most features.<commit_after><|endoftext|>


<commit_before>package led

import (
	"fmt"
	"github.com/boombuler/hid"
	"image/color"
)

type DeviceType int

func (dt DeviceType) String() string {
	idx := int(dt)
	if idx < 0 || idx >= len(drivers) {
		return ""
	}
	return drivers[idx].name()
}

type driver interface {
	name() string
	convert(hDev *hid.DeviceInfo) DeviceInfo
}

var drivers = []driver{}

func addDriver(drv driver) DeviceType {
	dt := DeviceType(len(drivers))
	drivers = append(drivers, drv)
	return dt
}

type Device interface {
	SetColor(c color.Color) error
	Close()
}

type DeviceInfo interface {
	GetPath() string
	GetType() DeviceType
	Open() (Device, error)
}

func Devices() <-chan DeviceInfo {
	result := make(chan DeviceInfo)
	go func() {
		for d := range hid.Devices() {
			di := toLedDeviceInfo(d)
			if di != nil {
				result <- di
			}
		}
		close(result)
	}()
	return result
}

func ByPath(path string) (DeviceInfo, error) {
	hd, err := hid.ByPath(path)
	if err != nil {
		return nil, err
	}
	led := toLedDeviceInfo(hd)
	if led == nil {
		return nil, fmt.Errorf("Unknown LED device (VID: %v, PID: %v)", hd.VendorId, hd.ProductId)
	}
	return led, nil
}

func toLedDeviceInfo(dev *hid.DeviceInfo) DeviceInfo {
	for _, drv := range drivers {
		di := drv.convert(dev)
		if di != nil {
			return di
		}
	}
	return nil
}
<commit_msg>added documentation<commit_after>package led

import (
	"fmt"
	"github.com/boombuler/hid"
	"image/color"
)

// Device type identifies the device type. the IDs may change on each program start.
type DeviceType int

// String returns a representation of the device type
func (dt DeviceType) String() string {
	idx := int(dt)
	if idx < 0 || idx >= len(drivers) {
		return ""
	}
	return drivers[idx].name()
}

type driver interface {
	name() string
	convert(hDev *hid.DeviceInfo) DeviceInfo
}

var drivers = []driver{}

func addDriver(drv driver) DeviceType {
	dt := DeviceType(len(drivers))
	drivers = append(drivers, drv)
	return dt
}

// Device is an opened LED device.
type Device interface {
	// SetColor sets the color of the LED to the closest supported color.
	SetColor(c color.Color) error
	// Close the device and release all resources
	Close()
}

// DeviceInfo keeps information about a physical LED device
type DeviceInfo interface {
	// GetPath returns a system specific path which can be used to find the device
	GetPath() string
	// GetType returns the "driver type" of the device
	GetType() DeviceType
	// Open opens the device for usage
	Open() (Device, error)
}

// Devices returns a channel with all connected LED devices
func Devices() <-chan DeviceInfo {
	result := make(chan DeviceInfo)
	go func() {
		for d := range hid.Devices() {
			di := toLedDeviceInfo(d)
			if di != nil {
				result <- di
			}
		}
		close(result)
	}()
	return result
}

// ByPath searches a device by given system specific path. (The path can be obtained from the GetPath func from DeviceInfo)
func ByPath(path string) (DeviceInfo, error) {
	hd, err := hid.ByPath(path)
	if err != nil {
		return nil, err
	}
	led := toLedDeviceInfo(hd)
	if led == nil {
		return nil, fmt.Errorf("Unknown LED device (VID: %v, PID: %v)", hd.VendorId, hd.ProductId)
	}
	return led, nil
}

func toLedDeviceInfo(dev *hid.DeviceInfo) DeviceInfo {
	for _, drv := range drivers {
		di := drv.convert(dev)
		if di != nil {
			return di
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package led

import (
	"fmt"
	"github.com/boombuler/hid"
	"image/color"
)

type DeviceType int

func (dt DeviceType) String() string {
	idx := int(dt)
	if idx < 0 || idx >= len(drivers) {
		return ""
	}
	return drivers[idx].name()
}

type driver interface {
	name() string
	convert(hDev *hid.DeviceInfo) DeviceInfo
}

var drivers = []driver{}

func addDriver(drv driver) DeviceType {
	dt := DeviceType(len(drivers))
	drivers = append(drivers, drv)
	return dt
}

type Device interface {
	SetColor(c color.Color) error
	Close()
}

type DeviceInfo interface {
	GetPath() string
	GetType() DeviceType
	Open() (Device, error)
}

func Devices() <-chan DeviceInfo {
	result := make(chan DeviceInfo)
	go func() {
		for d := range hid.Devices() {
			di := toLedDeviceInfo(d)
			if di != nil {
				result <- di
			}
		}
		close(result)
	}()
	return result
}

func ByPath(path string) (DeviceInfo, error) {
	hd, err := hid.ByPath(path)
	if err != nil {
		return nil, err
	}
	led := toLedDeviceInfo(hd)
	if led == nil {
		return nil, fmt.Errorf("Unknown LED device (VID: %v, PID: %v)", hd.VendorId, hd.ProductId)
	}
	return led, nil
}

func toLedDeviceInfo(dev *hid.DeviceInfo) DeviceInfo {
	for _, drv := range drivers {
		di := drv.convert(dev)
		if di != nil {
			return di
		}
	}
	return nil
}
<commit_msg>fixed build<commit_after>package led

import (
	"fmt"
	"github.com/boombuler/hid"
	"image/color"
)

type DeviceType int

func (dt DeviceType) String() string {
	idx := int(dt)
	if idx < 0 || idx >= len(drivers) {
		return ""
	}
	return drivers[idx].name()
}

type driver interface {
	name() string
	convert(hDev *hid.DeviceInfo) DeviceInfo
}

var drivers = []driver{}

func addDriver(drv driver) DeviceType {
	dt := DeviceType(len(drivers))
	drivers = append(drivers, drv)
	return dt
}

type Device interface {
	SetColor(c color.Color) error
	Close()
}

type DeviceInfo interface {
	GetPath() string
	GetType() DeviceType
	Open() (Device, error)
}

func Devices() <-chan DeviceInfo {
	result := make(chan DeviceInfo)
	go func() {
		for d := range hid.Devices() {
			di := toLedDeviceInfo(d)
			if di != nil {
				result <- di
			}
		}
		close(result)
	}()
	return result
}

func ByPath(path string) (DeviceInfo, error) {
	hd := hid.ByPath(path)
	if hd != nil {
		return nil, err
	}
	led := toLedDeviceInfo(hd)
	if led == nil {
		return nil, fmt.Errorf("Unknown LED device (VID: %v, PID: %v)", hd.VendorId, hd.ProductId)
	}
	return led, nil
}

func toLedDeviceInfo(dev *hid.DeviceInfo) DeviceInfo {
	for _, drv := range drivers {
		di := drv.convert(dev)
		if di != nil {
			return di
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"

	log "github.com/Sirupsen/logrus"
)

const eof = -1

// next returns the next rune in the input.
func (l *lexer) next() rune {
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}

	r, w := utf8.DecodeRuneInString(l.input[l.pos:])

	l.mark = r
	l.width = w

	l.pos += l.width

	return r
}

func (l *lexer) nextItem() item {
	item := <-l.items

	l.lastPos = item.pos

	return item
}

func (l *lexer) backup(pos int) {
	for i := 0; i < pos; i++ {
		l.pos -= l.width
		if l.pos < 0 {
			l.pos = 0
		}

		r, w := utf8.DecodeRuneInString(l.input[l.pos:])
		l.mark = r
		l.width = w
	}
}

func (l *lexer) peek(locs int) rune {
	pos := saveLexerPosition(l)

	var r rune

	x := 0

	for x < locs {
		l.next()

		if x == locs-1 {
			r = l.mark
		}

		x++
	}

	pos.restore(l)

	return r
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
	l.items <- item{t, l.start, l.input[l.start:l.pos]}

	l.start = l.pos
}

func (l *lexer) emitBefore(t itemType) {
	l.backup(1)

	if l.pos > l.start {
		l.emit(t)

		l.ignore()
	}
}

// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
	l.start = l.pos
}

// accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool {
	if strings.ContainsRune(valid, l.next()) {
		return true
	}

	l.backup(1)

	return false
}

// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
	count := 0

	for strings.ContainsRune(valid, l.next()) {
		count++
	}

	log.Debugf("Ending acceptRun after %d runes, last: %s", count, string(l.mark))

	l.backup(1)
}

// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}

	return nil
}

// lex creates a new scanner for the input string.
func lex(input string) *lexer {
	l := &lexer{
		input: input,
		items: make(chan item),
	}

	go l.run()

	return l
}

// run runs the state machine for the lexer.
func (l *lexer) run() {
	for l.state = lexText; l.state != nil; {
		l.state = l.state(l)
	}

	close(l.items)
}

func lexText(l *lexer) stateFn {
	log.Debugf("lexText %q", l.input[l.pos:])

	l.width = 0

Loop:
	for {
		//switch l.next() {
		switch r := l.next(); {
		case r == eof:
			break Loop
		case r == '%':
			l.emitBefore(itemText)

			l.next()

			if l.accept("s") {
				l.emit(itemRawString)
			}
		case (r == 'i' && strings.HasPrefix(l.input[l.pos:], "f(")) ||
			(r == 'e' && strings.HasPrefix(l.input[l.pos:], "n(")):
			l.emitBefore(itemText)

			return lexScript
		case r == '\\':
			r = l.peek(1)

			if r == '\\' {
				l.emitBefore(itemText)

				return lexScript
			}

			// These shouldn't even appear but google has sent me these back in translations
			if r == 'u' {
				log.Debug("Found escaped rune")

				l.emitBefore(itemText)

				l.next()

				l.acceptRun("u0123456789")

				l.ignore()
			}

			if r == 'n' {
				log.Debug("Found escaped newline")

				l.emitBefore(itemText)

				l.next()

				l.accept("n")

				l.acceptRun("[0123456789]")

				l.emit(itemRawString)
			}

		case strings.ContainsRune("\u3000・！？。…【】「」『』\n()", r) || unicode.IsSymbol(r):
			l.emitBefore(itemText)

			l.next()

			l.emit(itemRawString)

			return lexText
		}
	}

	if l.pos > l.start {
		l.emit(itemText)

		l.ignore()
	}

	l.emit(itemEOF)

	return nil

}

func lexScript(l *lexer) stateFn {
	log.Debugf("lexScript %q", l.input[l.pos:])

Loop:
	for {
		switch l.next() {
		case eof:
			log.Warn("Script not terminated properly %q", l.input[l.start:])
			break Loop
		case '(':
			l.emit(itemLeftParen)

			l.parenDepth++

			return lexInsideAction
		case '[':
			l.backup(1)
			l.emit(itemScript)
			return lexLeftDelim
		case '\\':
			if l.accept("!<>blgnrt{}$BIG.|^") {
				log.Debug("Found escaped ", string(l.mark))

				if l.mark == 'n' {
					l.acceptRun("[0123456789]")
				}

				break Loop
			}

			if l.accept("u") {
				l.acceptRun("u0123456789")

				break Loop
			}

			if l.accept("/") {
				// Are they just escaping them wrong?
				if l.accept("BIG") {
					break Loop
				}
			}
		case '\n':
			l.acceptRun("[0123456789]")

			break Loop

		default:
			log.Debug(string(l.mark))
		}
	}

	l.emit(itemScript)

	return lexText
}

func lexLeftDelim(l *lexer) stateFn {
	l.next()

	log.Debug("leftDelim: ", string(l.mark))

	l.emit(itemLeftDelim)

	return lexInsideAction
}

func lexRightDelim(l *lexer) stateFn {
	l.next()

	log.Debug("rightDelim: ", string(l.mark))

	l.emit(itemRightDelim)

	log.Debug("Paren depth: ", l.parenDepth)

	if l.parenDepth == 0 {
		return lexText
	}

	return lexInsideAction
}

// lexInsideAction scans the elements inside action delimiters.
func lexInsideAction(l *lexer) stateFn {
	log.Debugf("lexInsideAction %q", l.input[l.pos:])

	switch r := l.next(); {
	case r == eof:
		return l.errorf("unclosed action")
	case r == '(':
		l.emit(itemLeftParen)
		l.parenDepth++
	case r == ')':
		l.emit(itemRightParen)
		l.parenDepth--

		if l.parenDepth < 0 {
			return l.errorf("unexpected right paren %#U", r)
		}

		if l.parenDepth == 0 {
			return lexText
		}
	case r == '[':
		l.emitBefore(itemParameter)

		return lexLeftDelim
	case r == ']':
		l.emitBefore(itemParameter)

		return lexRightDelim
	case r == '%' && l.accept("s"):
		l.emit(itemParameter)
	case r == '"':
		l.emit(itemParameter)

		return lexText
	}

	return lexInsideAction
}
<commit_msg>Add more runes to handle as raw string<commit_after>package main

import (
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"

	log "github.com/Sirupsen/logrus"
)

const eof = -1

// next returns the next rune in the input.
func (l *lexer) next() rune {
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}

	r, w := utf8.DecodeRuneInString(l.input[l.pos:])

	l.mark = r
	l.width = w

	l.pos += l.width

	return r
}

func (l *lexer) nextItem() item {
	item := <-l.items

	l.lastPos = item.pos

	return item
}

func (l *lexer) backup(pos int) {
	for i := 0; i < pos; i++ {
		l.pos -= l.width
		if l.pos < 0 {
			l.pos = 0
		}

		r, w := utf8.DecodeRuneInString(l.input[l.pos:])
		l.mark = r
		l.width = w
	}
}

func (l *lexer) peek(locs int) rune {
	pos := saveLexerPosition(l)

	var r rune

	x := 0

	for x < locs {
		l.next()

		if x == locs-1 {
			r = l.mark
		}

		x++
	}

	pos.restore(l)

	return r
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
	l.items <- item{t, l.start, l.input[l.start:l.pos]}

	l.start = l.pos
}

func (l *lexer) emitBefore(t itemType) {
	l.backup(1)

	if l.pos > l.start {
		l.emit(t)

		l.ignore()
	}
}

// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
	l.start = l.pos
}

// accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool {
	if strings.ContainsRune(valid, l.next()) {
		return true
	}

	l.backup(1)

	return false
}

// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
	count := 0

	for strings.ContainsRune(valid, l.next()) {
		count++
	}

	log.Debugf("Ending acceptRun after %d runes, last: %s", count, string(l.mark))

	l.backup(1)
}

// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}

	return nil
}

// lex creates a new scanner for the input string.
func lex(input string) *lexer {
	l := &lexer{
		input: input,
		items: make(chan item),
	}

	go l.run()

	return l
}

// run runs the state machine for the lexer.
func (l *lexer) run() {
	for l.state = lexText; l.state != nil; {
		l.state = l.state(l)
	}

	close(l.items)
}

func lexText(l *lexer) stateFn {
	log.Debugf("lexText %q", l.input[l.pos:])

	l.width = 0

Loop:
	for {
		//switch l.next() {
		switch r := l.next(); {
		case r == eof:
			break Loop
		case r == '%':
			l.emitBefore(itemText)

			l.next()

			if l.accept("s") {
				l.emit(itemRawString)
			}
		case (r == 'i' && strings.HasPrefix(l.input[l.pos:], "f(")) ||
			(r == 'e' && strings.HasPrefix(l.input[l.pos:], "n(")):
			l.emitBefore(itemText)

			return lexScript
		case r == '\\':
			r = l.peek(1)

			if r == '\\' {
				l.emitBefore(itemText)

				return lexScript
			}

			// These shouldn't even appear but google has sent me these back in translations
			if r == 'u' {
				log.Debug("Found escaped rune")

				l.emitBefore(itemText)

				l.next()

				l.acceptRun("u0123456789")

				l.ignore()
			}

			if r == 'n' {
				log.Debug("Found escaped newline")

				l.emitBefore(itemText)

				l.next()

				l.accept("n")

				l.acceptRun("[0123456789]")

				l.emit(itemRawString)
			}

		case strings.ContainsRune("\u3000（）・！？。…【】「」『』\n()", r) || unicode.IsSymbol(r):
			l.emitBefore(itemText)

			l.next()

			l.emit(itemRawString)

			return lexText
		}
	}

	if l.pos > l.start {
		l.emit(itemText)

		l.ignore()
	}

	l.emit(itemEOF)

	return nil

}

func lexScript(l *lexer) stateFn {
	log.Debugf("lexScript %q", l.input[l.pos:])

Loop:
	for {
		switch l.next() {
		case eof:
			log.Warn("Script not terminated properly %q", l.input[l.start:])
			break Loop
		case '(':
			l.emit(itemLeftParen)

			l.parenDepth++

			return lexInsideAction
		case '[':
			l.backup(1)
			l.emit(itemScript)
			return lexLeftDelim
		case '\\':
			if l.accept("!<>blgnrt{}$BIG.|^") {
				log.Debug("Found escaped ", string(l.mark))

				if l.mark == 'n' {
					l.acceptRun("[0123456789]")
				}

				break Loop
			}

			if l.accept("u") {
				l.acceptRun("u0123456789")

				break Loop
			}

			if l.accept("/") {
				// Are they just escaping them wrong?
				if l.accept("BIG") {
					break Loop
				}
			}
		case '\n':
			l.acceptRun("[0123456789]")

			break Loop

		default:
			log.Debug(string(l.mark))
		}
	}

	l.emit(itemScript)

	return lexText
}

func lexLeftDelim(l *lexer) stateFn {
	l.next()

	log.Debug("leftDelim: ", string(l.mark))

	l.emit(itemLeftDelim)

	return lexInsideAction
}

func lexRightDelim(l *lexer) stateFn {
	l.next()

	log.Debug("rightDelim: ", string(l.mark))

	l.emit(itemRightDelim)

	log.Debug("Paren depth: ", l.parenDepth)

	if l.parenDepth == 0 {
		return lexText
	}

	return lexInsideAction
}

// lexInsideAction scans the elements inside action delimiters.
func lexInsideAction(l *lexer) stateFn {
	log.Debugf("lexInsideAction %q", l.input[l.pos:])

	switch r := l.next(); {
	case r == eof:
		return l.errorf("unclosed action")
	case r == '(':
		l.emit(itemLeftParen)
		l.parenDepth++
	case r == ')':
		l.emit(itemRightParen)
		l.parenDepth--

		if l.parenDepth < 0 {
			return l.errorf("unexpected right paren %#U", r)
		}

		if l.parenDepth == 0 {
			return lexText
		}
	case r == '[':
		l.emitBefore(itemParameter)

		return lexLeftDelim
	case r == ']':
		l.emitBefore(itemParameter)

		return lexRightDelim
	case r == '%' && l.accept("s"):
		l.emit(itemParameter)
	case r == '"':
		l.emit(itemParameter)

		return lexText
	}

	return lexInsideAction
}
<|endoftext|>


<commit_before>// Copyright 2013 Frank Schroeder. All rights reserved. MIT licensed.
//
// Parts of the lexer are taken from the template/text/parser package
// For these parts the following applies:
//
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file of the go 1.2
// distribution.

package properties

import (
	"fmt"
	// "log"
	"strings"
	// "unicode"
	"strconv"
	"unicode/utf8"
)

// item represents a token or text string returned from the scanner.
type item struct {
	typ itemType // The type of this item.
	pos Pos      // The starting position, in bytes, of this item in the input string.
	val string   // The value of this item.
}

func (i item) String() string {
	switch {
	case i.typ == itemEOF:
		return "EOF"
	case i.typ == itemError:
		return i.val
	case len(i.val) > 10:
		return fmt.Sprintf("%.10q...", i.val)
	}
	return fmt.Sprintf("%q", i.val)
}

type Pos int

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

const (
	itemError itemType = iota // error occurred; value is text of error
	itemEOF
	itemDelim // a = or : delimiter char
	itemKey   // a key
	itemValue // a value
)

const eof = -1

const whitespace = " \f\t"

// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*lexer) stateFn

// lexer holds the state of the scanner.
type lexer struct {
	input   string    // the string being scanned
	state   stateFn   // the next lexing function to enter
	pos     Pos       // current position in the input
	start   Pos       // start position of this item
	width   Pos       // width of last rune read from input
	lastPos Pos       // position of most recent item returned by nextItem
	runes   []rune    // scanned runes for this item
	items   chan item // channel of scanned items
}

// next returns the next rune in the input.
func (l *lexer) next() rune {
	if int(l.pos) >= len(l.input) {
		l.width = 0
		return eof
	}
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	l.width = Pos(w)
	l.pos += l.width
	return r
}

// peek returns but does not consume the next rune in the input.
func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

// backup steps back one rune. Can only be called once per call of next.
func (l *lexer) backup() {
	l.pos -= l.width
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
	item := item{t, l.start, string(l.runes)}
	// log.Printf("lex.emit: %s", item)
	l.items <- item
	l.start = l.pos
	l.runes = l.runes[:0]
}

// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
	l.start = l.pos
}

// appends the rune to the current value
func (l *lexer) appendRune(r rune) {
	l.runes = append(l.runes, r)
}

// accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
	for strings.IndexRune(valid, l.next()) >= 0 {
	}
	l.backup()
}

// acceptRunUntil consumes a run of runes up to a terminator.
func (l *lexer) acceptRunUntil(term rune) {
	for term != l.next() {
	}
	l.backup()
}

// hasText returns true if the current parsed text is not empty.
func (l *lexer) isNotEmpty() bool {
	return l.pos > l.start
}

// lineNumber reports which line we're on, based on the position of
// the previous item returned by nextItem. Doing it this way
// means we don't have to worry about peek double counting.
func (l *lexer) lineNumber() int {
	return 1 + strings.Count(l.input[:l.lastPos], "\n")
}

// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
	return nil
}

// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
	item := <-l.items
	l.lastPos = item.pos
	return item
}

// lex creates a new scanner for the input string.
func lex(input string) *lexer {
	l := &lexer{
		input: input,
		items: make(chan item),
		runes: make([]rune, 0, 32),
	}
	go l.run()
	return l
}

// run runs the state machine for the lexer.
func (l *lexer) run() {
	for l.state = lexBeforeKey(l); l.state != nil; {
		l.state = l.state(l)
	}
}

// state functions
// TODO: handle comments

// lexBeforeKey scans until a key begins.
func lexBeforeKey(l *lexer) stateFn {
	switch r := l.next(); {
	case isEOF(r):
		l.emit(itemEOF)
		return nil

	case isEOL(r):
		l.ignore()
		return lexBeforeKey

	case isComment(r):
		l.acceptRunUntil('\n')
		l.ignore()
		return lexBeforeKey

	case isWhitespace(r):
		l.acceptRun(whitespace)
		l.ignore()
		return lexKey

	default:
		l.backup()
		return lexKey
	}

}

// lexKey scans the key up to a delimiter
func lexKey(l *lexer) stateFn {
Loop:
	for {
		switch r := l.next(); {

		case isEscape(r):
			err := l.scanEscapeSequence()
			if err != nil {
				return l.errorf(err.Error())
			}

		case isKeyTerminationCharacter(r):
			l.backup()
			break Loop

		case isEOF(r):
			return l.errorf("premature EOF")

		default:
			l.appendRune(r)
		}
	}

	if len(l.runes) > 0 {
		l.emit(itemKey)
	}

	// ignore trailing spaces
	l.acceptRun(" ")
	l.ignore()

	return lexDelim
}

// lexDelim scans the delimiter. We expect to be just before the delimiter.
func lexDelim(l *lexer) stateFn {
	if l.next() == eof {
		return l.errorf("premature EOF")
	}
	l.emit(itemDelim)
	return lexValue
}

// lexValue scans text until the end of the line. We expect to be just after the delimiter.
func lexValue(l *lexer) stateFn {
	// ignore leading whitespace
	l.acceptRun(whitespace)
	l.ignore()

	// TODO: handle multiline with indent on subsequent lines
	for {
		switch r := l.next(); {
		case isEscape(r):
			r := l.peek()
			if isEOL(r) {
				l.next()
				l.acceptRun(whitespace)
			} else {
				err := l.scanEscapeSequence()
				if err != nil {
					return l.errorf(err.Error())
				}
			}

		case isEOL(r):
			l.emit(itemValue)

			// ignore the new line
			l.ignore()
			return lexBeforeKey

		case isEOF(r):
			l.emit(itemValue)
			l.emit(itemEOF)
			return nil

		default:
			l.appendRune(r)
		}
	}
}

// scanEscapeSequence scans either one of the escaped characters
// or a unicode literal. We expect to be after the escape character.
func (l *lexer) scanEscapeSequence() error {
	switch r := l.next(); {

	case isEscapedCharacter(r):
		l.appendRune(decodeEscapedCharacter(r))
		return nil

	case atUnicodeLiteral(r):
		return l.scanUnicodeLiteral()

	case isEOF(r):
		return fmt.Errorf("premature EOF")

	default:
		return fmt.Errorf("invalid escape sequence %s", string(r))
	}
}

// scans a unicode literal in \[uU]XXXX form. We expect to be after the \[uU].
func (l *lexer) scanUnicodeLiteral() error {
	// scan the digits
	d := make([]rune, 4)
	for i := 0; i < 4; i++ {
		d[i] = l.next()
		if d[i] == eof {
			return fmt.Errorf("premature EOF")
		}
		if !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) {
			return fmt.Errorf("invalid unicode literal")
		}
	}

	// decode the digits into a rune
	r, err := strconv.ParseInt(string(d), 16, 0)
	if err != nil {
		return err
	}
	l.appendRune(rune(r))
	return nil
}

// decodeEscapedCharacter returns the rune the unescaped value for the
// escaped rune (minus escape character).
func decodeEscapedCharacter(r rune) rune {
	switch r {
	case 'n':
		return '\n'
	case 'r':
		return '\r'
	case 't':
		return '\t'
	default:
		return r
	}
}

// atUnicodeLiteral reports whether we are at a unicode literal.
// The escape character has already been consumed.
func atUnicodeLiteral(r rune) bool {
	return r == 'u'
}

// isComment reports whether we are at the start of a comment.
func isComment(r rune) bool {
	return r == '#' || r == '!'
}

// isEOF reports whether we are at EOF.
func isEOF(r rune) bool {
	return r == eof
}

// isEOL reports whether we are at a new line character.
func isEOL(r rune) bool {
	return r == '\n'
}

// isEscape reports whether the rune is the escape character which
// prefixes unicode literals and other escaped characters.
func isEscape(r rune) bool {
	return r == '\\'
}

// isEscapedCharacter reports whether we are at one of the characters that need escaping.
// The escape character has already been consumed.
func isEscapedCharacter(r rune) bool {
	return strings.ContainsRune(" :=nrt", r)
}

// isKeyTerminationCharacter reports whether the rune terminates the current key.
func isKeyTerminationCharacter(r rune) bool {
	return strings.ContainsRune(" :=", r)
}

// isWhitespace reports whether the rune is a whitespace character.
func isWhitespace(r rune) bool {
	return strings.ContainsRune(whitespace, r)
}
<commit_msg>Cleanup<commit_after>// Copyright 2013 Frank Schroeder. All rights reserved. MIT licensed.
//
// Parts of the lexer are taken from the template/text/parser package
// For these parts the following applies:
//
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file of the go 1.2
// distribution.

package properties

import (
	"fmt"
	"strconv"
	"strings"
	"unicode/utf8"
)

// item represents a token or text string returned from the scanner.
type item struct {
	typ itemType // The type of this item.
	pos Pos      // The starting position, in bytes, of this item in the input string.
	val string   // The value of this item.
}

func (i item) String() string {
	switch {
	case i.typ == itemEOF:
		return "EOF"
	case i.typ == itemError:
		return i.val
	case len(i.val) > 10:
		return fmt.Sprintf("%.10q...", i.val)
	}
	return fmt.Sprintf("%q", i.val)
}

type Pos int

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

const (
	itemError itemType = iota // error occurred; value is text of error
	itemEOF
	itemDelim // a = or : delimiter char
	itemKey   // a key
	itemValue // a value
)

const eof = -1

const whitespace = " \f\t"

// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*lexer) stateFn

// lexer holds the state of the scanner.
type lexer struct {
	input   string    // the string being scanned
	state   stateFn   // the next lexing function to enter
	pos     Pos       // current position in the input
	start   Pos       // start position of this item
	width   Pos       // width of last rune read from input
	lastPos Pos       // position of most recent item returned by nextItem
	runes   []rune    // scanned runes for this item
	items   chan item // channel of scanned items
}

// next returns the next rune in the input.
func (l *lexer) next() rune {
	if int(l.pos) >= len(l.input) {
		l.width = 0
		return eof
	}
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	l.width = Pos(w)
	l.pos += l.width
	return r
}

// peek returns but does not consume the next rune in the input.
func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

// backup steps back one rune. Can only be called once per call of next.
func (l *lexer) backup() {
	l.pos -= l.width
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
	item := item{t, l.start, string(l.runes)}
	// log.Printf("lex.emit: %s", item)
	l.items <- item
	l.start = l.pos
	l.runes = l.runes[:0]
}

// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
	l.start = l.pos
}

// appends the rune to the current value
func (l *lexer) appendRune(r rune) {
	l.runes = append(l.runes, r)
}

// accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
	for strings.IndexRune(valid, l.next()) >= 0 {
	}
	l.backup()
}

// acceptRunUntil consumes a run of runes up to a terminator.
func (l *lexer) acceptRunUntil(term rune) {
	for term != l.next() {
	}
	l.backup()
}

// hasText returns true if the current parsed text is not empty.
func (l *lexer) isNotEmpty() bool {
	return l.pos > l.start
}

// lineNumber reports which line we're on, based on the position of
// the previous item returned by nextItem. Doing it this way
// means we don't have to worry about peek double counting.
func (l *lexer) lineNumber() int {
	return 1 + strings.Count(l.input[:l.lastPos], "\n")
}

// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
	return nil
}

// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
	item := <-l.items
	l.lastPos = item.pos
	return item
}

// lex creates a new scanner for the input string.
func lex(input string) *lexer {
	l := &lexer{
		input: input,
		items: make(chan item),
		runes: make([]rune, 0, 32),
	}
	go l.run()
	return l
}

// run runs the state machine for the lexer.
func (l *lexer) run() {
	for l.state = lexBeforeKey(l); l.state != nil; {
		l.state = l.state(l)
	}
}

// state functions

// lexBeforeKey scans until a key begins.
func lexBeforeKey(l *lexer) stateFn {
	switch r := l.next(); {
	case isEOF(r):
		l.emit(itemEOF)
		return nil

	case isEOL(r):
		l.ignore()
		return lexBeforeKey

	case isComment(r):
		l.acceptRunUntil('\n')
		l.ignore()
		return lexBeforeKey

	case isWhitespace(r):
		l.acceptRun(whitespace)
		l.ignore()
		return lexKey

	default:
		l.backup()
		return lexKey
	}

}

// lexKey scans the key up to a delimiter
func lexKey(l *lexer) stateFn {
Loop:
	for {
		switch r := l.next(); {

		case isEscape(r):
			err := l.scanEscapeSequence()
			if err != nil {
				return l.errorf(err.Error())
			}

		case isKeyTerminationCharacter(r):
			l.backup()
			break Loop

		case isEOF(r):
			return l.errorf("premature EOF")

		default:
			l.appendRune(r)
		}
	}

	if len(l.runes) > 0 {
		l.emit(itemKey)
	}

	// ignore trailing spaces
	l.acceptRun(" ")
	l.ignore()

	return lexDelim
}

// lexDelim scans the delimiter. We expect to be just before the delimiter.
func lexDelim(l *lexer) stateFn {
	if l.next() == eof {
		return l.errorf("premature EOF")
	}
	l.emit(itemDelim)
	return lexValue
}

// lexValue scans text until the end of the line. We expect to be just after the delimiter.
func lexValue(l *lexer) stateFn {
	// ignore leading whitespace
	l.acceptRun(whitespace)
	l.ignore()

	// TODO: handle multiline with indent on subsequent lines
	for {
		switch r := l.next(); {
		case isEscape(r):
			r := l.peek()
			if isEOL(r) {
				l.next()
				l.acceptRun(whitespace)
			} else {
				err := l.scanEscapeSequence()
				if err != nil {
					return l.errorf(err.Error())
				}
			}

		case isEOL(r):
			l.emit(itemValue)

			// ignore the new line
			l.ignore()
			return lexBeforeKey

		case isEOF(r):
			l.emit(itemValue)
			l.emit(itemEOF)
			return nil

		default:
			l.appendRune(r)
		}
	}
}

// scanEscapeSequence scans either one of the escaped characters
// or a unicode literal. We expect to be after the escape character.
func (l *lexer) scanEscapeSequence() error {
	switch r := l.next(); {

	case isEscapedCharacter(r):
		l.appendRune(decodeEscapedCharacter(r))
		return nil

	case atUnicodeLiteral(r):
		return l.scanUnicodeLiteral()

	case isEOF(r):
		return fmt.Errorf("premature EOF")

	default:
		return fmt.Errorf("invalid escape sequence %s", string(r))
	}
}

// scans a unicode literal in \[uU]XXXX form. We expect to be after the \[uU].
func (l *lexer) scanUnicodeLiteral() error {
	// scan the digits
	d := make([]rune, 4)
	for i := 0; i < 4; i++ {
		d[i] = l.next()
		if d[i] == eof {
			return fmt.Errorf("premature EOF")
		}
		if !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) {
			return fmt.Errorf("invalid unicode literal")
		}
	}

	// decode the digits into a rune
	r, err := strconv.ParseInt(string(d), 16, 0)
	if err != nil {
		return err
	}
	l.appendRune(rune(r))
	return nil
}

// decodeEscapedCharacter returns the rune the unescaped value for the
// escaped rune (minus escape character).
func decodeEscapedCharacter(r rune) rune {
	switch r {
	case 'n':
		return '\n'
	case 'r':
		return '\r'
	case 't':
		return '\t'
	default:
		return r
	}
}

// atUnicodeLiteral reports whether we are at a unicode literal.
// The escape character has already been consumed.
func atUnicodeLiteral(r rune) bool {
	return r == 'u'
}

// isComment reports whether we are at the start of a comment.
func isComment(r rune) bool {
	return r == '#' || r == '!'
}

// isEOF reports whether we are at EOF.
func isEOF(r rune) bool {
	return r == eof
}

// isEOL reports whether we are at a new line character.
func isEOL(r rune) bool {
	return r == '\n'
}

// isEscape reports whether the rune is the escape character which
// prefixes unicode literals and other escaped characters.
func isEscape(r rune) bool {
	return r == '\\'
}

// isEscapedCharacter reports whether we are at one of the characters that need escaping.
// The escape character has already been consumed.
func isEscapedCharacter(r rune) bool {
	return strings.ContainsRune(" :=nrt", r)
}

// isKeyTerminationCharacter reports whether the rune terminates the current key.
func isKeyTerminationCharacter(r rune) bool {
	return strings.ContainsRune(" :=", r)
}

// isWhitespace reports whether the rune is a whitespace character.
func isWhitespace(r rune) bool {
	return strings.ContainsRune(whitespace, r)
}
<|endoftext|>


<commit_before>// Copyright 2016 José Santos <henrique_1609@me.com>
//
// 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 main

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

type Service struct {
	DevPort string
	AppPort string
	Refresh string
	Dir     string

	Env      []string
	Commands map[string]*command
}

type command struct {
	Building       string
	Match, Command string
	Continue       string
	Oninit, Onexit string
	Dir            string
	Env            []string

	Wait, Stderr, Stdout bool

	pattern *regexp.Regexp

	running map[string]*exec.Cmd
}

func (s *Service) Init() {

	if _port != nil && *_port != "" {
		ports := strings.SplitN(*_port, ":", 2)
		s.DevPort = ports[0]

		if len(ports) > 1 {
			s.AppPort = ports[1]
		}

		if s.DevPort == "" {
			s.DevPort = "8080"
		}

		if s.AppPort == "" {
			port, _ := strconv.Atoi(s.AppPort)
			s.AppPort = fmt.Sprint(port + 1)
		}
	} else if s.DevPort != "" && s.AppPort == "" {
		s.AppPort = "8080"
	}

	if _tickerDuration != nil && *_tickerDuration != "" {
		s.Refresh = *_tickerDuration
	} else if s.Refresh == "" {
		s.Refresh = "2s"
	}

	s.Dir, _ = filepath.Abs(s.Dir)
}

func (s *Service) GetRoot() string {
	return s.Dir
}

func (s *Service) GetEnv() []string {
	return append(os.Environ(), s.Env...)
}
<commit_msg>use os.Getwd to get current working dir<commit_after>// Copyright 2016 José Santos <henrique_1609@me.com>
//
// 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 main

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

type Service struct {
	DevPort  string
	AppPort  string
	Refresh  string
	Dir      string

	Env      []string
	Commands map[string]*command
}

type command struct {
	Building             string
	Match, Command       string
	Continue             string
	Oninit, Onexit       string
	Dir                  string
	Env                  []string

	Wait, Stderr, Stdout bool

	pattern              *regexp.Regexp

	running              map[string]*exec.Cmd
}

func (s *Service) Init() {

	if _port != nil && *_port != "" {
		ports := strings.SplitN(*_port, ":", 2)
		s.DevPort = ports[0]

		if len(ports) > 1 {
			s.AppPort = ports[1]
		}

		if s.DevPort == "" {
			s.DevPort = "8080"
		}

		if s.AppPort == "" {
			port, _ := strconv.Atoi(s.AppPort)
			s.AppPort = fmt.Sprint(port + 1)
		}
	} else if s.DevPort != "" && s.AppPort == "" {
		s.AppPort = "8080"
	}

	if _tickerDuration != nil && *_tickerDuration != "" {
		s.Refresh = *_tickerDuration
	} else if s.Refresh == "" {
		s.Refresh = "2s"
	}

	if s.Dir == "" {
		s.Dir, _ = os.Getwd()
	} else {
		s.Dir, _ = filepath.Abs(s.Dir)
	}
}

func (s *Service) GetRoot() string {
	return s.Dir
}

func (s *Service) GetEnv() []string {
	return append(os.Environ(), s.Env...)
}
<|endoftext|>


<commit_before>// 23 june 2014
package main

import (
	"fmt"
	"os"
	"bytes"
)

func generate(ns Namespace) {
	b := new(bytes.Buffer)

	fmt.Fprintf(b, "package %s\n\nimport \"unsafe\"\nimport \"errors\"\n\n// ADD IMPORTS AND CGO DIRECTIVES HERE\n// BE SURE TO INCLUDE stdio.h\n\n", nsGoName(ns.Name))

	// enumerations
	// to avoid unnecessary typing, let's collect all value names
	// if, for any enum, at least one name is ambiguous, we require the first word of the enum name as a prefix
	namecount := map[string]int{}
	for _, n := range ns.TopLevelEnums {
		e := ns.Enums[n]
		if e.Namespace != ns.Name {		// skip foreign imports
			continue
		}
		for _, i := range e.Values {
			v := ns.Values[i]
			namecount[ns.GoName(v)]++
		}
	}
	for _, n := range ns.TopLevelEnums {
		e := ns.Enums[n]
		if e.Namespace != ns.Name {		// skip foreign imports
			continue
		}
		goName := ns.GoName(e)
		fmt.Fprintf(b, "type %s %s\n", goName, e.StorageType.BasicString())
		fmt.Fprintf(b, "const (\n")
		fgw := ""
		for _, i := range e.Values {
			v := ns.Values[i]
			if namecount[ns.GoName(v)] > 1 {
				fgw = firstGoWord(goName)
				break
			}
		}
		for _, i := range e.Values {
			v := ns.Values[i]
			fmt.Fprintf(b, "\t%s%s %s = C.%s\n",
				fgw, ns.GoName(v), goName, ns.CName(v))
		}
		fmt.Fprintf(b, ")\n")
		fmt.Fprintf(b, "\n")
	}

	// interfaces
	// we don't need to worry about implementations of methods for each object until we get to the objects themselves
	// we also don't need to worry about signals
	// we DO need to worry about prerequisite types, putting an I before object prerequisites
	for _, n := range ns.TopLevelInterfaces {
		ii := ns.Interfaces[n]
		if ii.Namespace != ns.Name {		// skip foreign imports
			continue
		}
		goName := ns.GoName(ii)
		fmt.Fprintf(b, "type %s interface {\n", goName)
		for _, p := range ii.Prerequisites {
			fmt.Fprintf(b, "\t%s\n", ns.GoIName(p))
		}
		for _, m := range ii.VFuncs {
			v := ns.VFuncs[m]
			fmt.Fprintf(b, "\tfunc %s\n", ns.GoFuncSig(v.CallableInfo))
		}
		fmt.Fprintf(b, "}\n")
		// TODO constants
		fmt.Fprintf(b, "\n")
	}

	// objects
	// all objects are either derived (embed the base class) or not (have a native member)
	for _, n := range ns.TopLevelObjects {
		o := ns.Objects[n]
		if o.Namespace != ns.Name {		// skip foreign imports
			continue
		}
		goName := ns.GoName(o)
		fmt.Fprintf(b, "type %s struct {\n", goName)
		if o.Parent == -1 {		// base
			fmt.Fprintf(b, "\tnative unsafe.Pointer\n")
			fmt.Fprintf(b, "}\n")
			fmt.Fprintf(b, "func (c *%s) Native() uintptr {\n", goName)
			fmt.Fprintf(b, "\treturn uintptr(c.native)\n");
		} else {
			oo := ns.Objects[o.Parent]
			fmt.Fprintf(b, "\t%s\n", ns.GoName(oo))
		}
		fmt.Fprintf(b, "}\n")
		for _, m := range o.Methods {
			mm := ns.Functions[m]
			fmt.Fprintf(b, "%s\n", ns.wrap(mm, o, false, InterfaceInfo{}))
		}
		for _, ii := range o.Interfaces {
			iii := ns.Interfaces[ii]
			for _, m := range iii.Methods {
				mm := ns.Functions[m]
				fmt.Fprintf(b, "%s\n", ns.wrap(mm, o, true, iii))
			}
		}
		// TODO other methods
		// TODO constants
		fmt.Fprintf(b, "\n")
	}

	os.Stdout.Write(b.Bytes())
}

// the rest of this file generates a wrapper function, taking care of converting special GLib constructs

var basicNames = map[TypeTag]string{
	TagVoid:			"unsafe.Pointer",
	TagBoolean:		"C.gboolean",
	TagInt8:			"C.gint8",
	TagUint8:			"C.guint8",
	TagInt16:			"C.gint16",
	TagUint16:		"C.guint16",
	TagInt32:			"C.gint32",
	TagUint32:		"C.guint32",
	TagInt64:			"C.gint64",
	TagUint64:		"C.guint64",
	TagFloat:			"C.gfloat",
	TagDouble:		"C.gdouble",
	TagGType:		"C.GType",
	TagUnichar:		"C.gunichar",
}

func (ns Namespace) argPrefix(arg ArgInfo, t TypeInfo) string {
	// no prefix needed
	if n, ok := basicNames[t.Tag]; ok {
		return fmt.Sprintf("\treal_%s := %s(%s)\n", arg.Name, n, arg.Name)
	}
	switch t.Tag {
	case TagUTF8String, TagFilename:
		s := fmt.Sprintf("\treal_%s := (*C.gchar)(unsafe.Pointer(C.CString(%s)))\n", arg.Name, arg.Name)
		s += fmt.Sprintf("\tdefer C.free(unsafe.Pointer(real_%s))\n", arg.Name)
		return s
	case TagArray:
		// TODO
	case TagInterface:
		ctype := ns.CName(t.Interface)
		return fmt.Sprintf("\treal_%s = (*C.%s)(unsafe.Pointer(%s.Native()))\n", arg.Name, ctype, arg.Name)
	case TagGList:
		// TODO
	case TagGSList:
		// TODO
	case TagGHashTable:
		// TODO
	case TagGError:
		return fmt.Sprintf("\tvar real_%s *C.GError = nil\n", arg.Name)
	default:
		panic(fmt.Errorf("unknown tag type %d in argPrefix()", t.Tag))
	}
	return "\t//TODO\n"
}

func (ns Namespace) argSuffix(arg ArgInfo, t TypeInfo) string {
	if t.Tag == TagGError {
		s := fmt.Sprintf("\tif real_%s != nil {\n", arg.Name)
		s += fmt.Sprintf("\t\tcmsg_%s := (*C.char)(unsafe.Pointer(real_%s.message))\n", arg.Name)
		s += fmt.Sprintf("\t\t%s = errors.New(C.GoString(cmsg_%s)\n", arg.Name, arg.Name)
		s += fmt.Sprintf("\t}\n")
		return s
	}
	return ""			// no extra cleanup needed
}

func (ns Namespace) wrap(method FunctionInfo, to ObjectInfo, isInterface bool, iface InterfaceInfo) string {
	s := "func "
	prefix := ""
	suffix := ""
	// method receivers aren't listed in the arguments; we have to fake it
	if method.IsMethod {
		// make a fake receiver
		receiver := ArgInfo{
			BaseInfo:		BaseInfo{
				Namespace:	ns.Name,
				Name:		"this",		// let's hope nothing uses this name
			},
		}
		rtype := TypeInfo{
			BaseInfo:		BaseInfo{
				Namespace:	ns.Name,
			},
			IsPointer:		true,
			Tag:			TagInterface,
			Interface:		to.BaseInfo,
		}
		itype := rtype
		if isInterface {
			itype.Interface = iface.BaseInfo
		}
		s += "("
		prefix += ns.argPrefix(receiver, itype)
		suffix = ns.argSuffix(receiver, itype) + suffix
		s += ns.ArgValueToGo(receiver, rtype, false)
		s += ") "
	}
	// disambiguate between constructors
	// a more Go-like way would be to insert the type name after the New but before anything else :/ conformal/gotk3 does it this way so meh
	if (method.Flags & FunctionIsConstructor) != 0 {
		s += ns.GoName(to)
	}
	s += ns.GoName(method) + "("
	for i := 0; i < len(method.Args); i++ {
		arg := ns.Args[method.Args[i]]
		prefix += ns.argPrefix(arg, ns.Types[arg.Type])
		suffix = ns.argSuffix(arg, ns.Types[arg.Type]) + suffix
		s += ns.ArgToGo(method.Args[i])
		s += ", "
	}
	s += ") "
	ret := ns.TypeToGo(method.ReturnType)
	if ret != "" {
		s += "(ret " + ret + ") "
	}
	s += "{\n"
	s += prefix
	s += "\t"
	if ret != "" {
		s += "ret = (" + ret + ")("
	}
	s += "C." + ns.CName(method) + "("
	if method.IsMethod {
		s += "real_this, "
	}
	for i := 0; i < len(method.Args); i++ {
		arg := ns.Args[method.Args[i]]
		s += "real_" + arg.Name + ", "
	}
	s += ")"
	if ret != "" {
		s += ")"
	}
	s += "\n"
	s += suffix
	if ret != "" {
		s += "\treturn ret\n"
	}
	s += "}"
	return s
}
<commit_msg>Added equivalent interfaces for objects.<commit_after>// 23 june 2014
package main

import (
	"fmt"
	"os"
	"bytes"
)

func generate(ns Namespace) {
	b := new(bytes.Buffer)

	fmt.Fprintf(b, "package %s\n\nimport \"unsafe\"\nimport \"errors\"\n\n// ADD IMPORTS AND CGO DIRECTIVES HERE\n// BE SURE TO INCLUDE stdio.h\n\n", nsGoName(ns.Name))

	// enumerations
	// to avoid unnecessary typing, let's collect all value names
	// if, for any enum, at least one name is ambiguous, we require the first word of the enum name as a prefix
	namecount := map[string]int{}
	for _, n := range ns.TopLevelEnums {
		e := ns.Enums[n]
		if e.Namespace != ns.Name {		// skip foreign imports
			continue
		}
		for _, i := range e.Values {
			v := ns.Values[i]
			namecount[ns.GoName(v)]++
		}
	}
	for _, n := range ns.TopLevelEnums {
		e := ns.Enums[n]
		if e.Namespace != ns.Name {		// skip foreign imports
			continue
		}
		goName := ns.GoName(e)
		fmt.Fprintf(b, "type %s %s\n", goName, e.StorageType.BasicString())
		fmt.Fprintf(b, "const (\n")
		fgw := ""
		for _, i := range e.Values {
			v := ns.Values[i]
			if namecount[ns.GoName(v)] > 1 {
				fgw = firstGoWord(goName)
				break
			}
		}
		for _, i := range e.Values {
			v := ns.Values[i]
			fmt.Fprintf(b, "\t%s%s %s = C.%s\n",
				fgw, ns.GoName(v), goName, ns.CName(v))
		}
		fmt.Fprintf(b, ")\n")
		fmt.Fprintf(b, "\n")
	}

	// interfaces
	// we don't need to worry about implementations of methods for each object until we get to the objects themselves
	// we also don't need to worry about signals
	// we DO need to worry about prerequisite types, putting an I before object prerequisites
	for _, n := range ns.TopLevelInterfaces {
		ii := ns.Interfaces[n]
		if ii.Namespace != ns.Name {		// skip foreign imports
			continue
		}
		goName := ns.GoName(ii)
		fmt.Fprintf(b, "type %s interface {\n", goName)
		for _, p := range ii.Prerequisites {
			fmt.Fprintf(b, "\t%s\n", ns.GoIName(p))
		}
		for _, m := range ii.VFuncs {
			v := ns.VFuncs[m]
			fmt.Fprintf(b, "\tfunc %s\n", ns.GoFuncSig(v.CallableInfo))
		}
		fmt.Fprintf(b, "}\n")
		// TODO constants
		fmt.Fprintf(b, "\n")
	}

	// objects
	// all objects are either derived (embed the base class) or not (have a native member)
	// each object also gets the methods of the interfaces it implements
	// each object ALSO gets its own interface, to play into the whole polymorphism thing
	for _, n := range ns.TopLevelObjects {
		o := ns.Objects[n]
		if o.Namespace != ns.Name {		// skip foreign imports
			continue
		}
		goName := ns.GoName(o)
		goIName := ns.GoIName(o)
		fmt.Fprintf(b, "type %s struct {\n", goName)
		if o.Parent == -1 {		// base
			fmt.Fprintf(b, "\tnative unsafe.Pointer\n")
			fmt.Fprintf(b, "}\n")
			fmt.Fprintf(b, "func (c *%s) Native() uintptr {\n", goName)
			fmt.Fprintf(b, "\treturn uintptr(c.native)\n");
		} else {
			oo := ns.Objects[o.Parent]
			fmt.Fprintf(b, "\t%s\n", ns.GoName(oo))
		}
		fmt.Fprintf(b, "}\n")
		for _, m := range o.Methods {
			mm := ns.Functions[m]
			fmt.Fprintf(b, "%s\n", ns.wrap(mm, o, false, InterfaceInfo{}))
		}
		for _, ii := range o.Interfaces {
			iii := ns.Interfaces[ii]
			for _, m := range iii.Methods {
				mm := ns.Functions[m]
				fmt.Fprintf(b, "%s\n", ns.wrap(mm, o, true, iii))
			}
		}
		// TODO other methods
		fmt.Fprintf(b, "type %s interface {\n", goIName)
		if o.Parent != -1 {
			oo := ns.Objects[o.Parent]
			fmt.Fprintf(b, "\t%s\n", ns.GoIName(oo))
		}
		for _, ii := range o.Interfaces {
			iii := ns.Interfaces[ii]
			fmt.Fprintf(b, "\t%s\n", ns.GoName(iii))
		}
		for _, m := range o.Methods {
			f := ns.Functions[m]
			if f.IsMethod {			// only actual methods
				fmt.Fprintf(b, "\tfunc %s\n", ns.GoFuncSig(f.CallableInfo))
			}
		}
		fmt.Fprintf(b, "}\n")
		// TODO constants
		fmt.Fprintf(b, "\n")
	}

	os.Stdout.Write(b.Bytes())
}

// the rest of this file generates a wrapper function, taking care of converting special GLib constructs

var basicNames = map[TypeTag]string{
	TagVoid:			"unsafe.Pointer",
	TagBoolean:		"C.gboolean",
	TagInt8:			"C.gint8",
	TagUint8:			"C.guint8",
	TagInt16:			"C.gint16",
	TagUint16:		"C.guint16",
	TagInt32:			"C.gint32",
	TagUint32:		"C.guint32",
	TagInt64:			"C.gint64",
	TagUint64:		"C.guint64",
	TagFloat:			"C.gfloat",
	TagDouble:		"C.gdouble",
	TagGType:		"C.GType",
	TagUnichar:		"C.gunichar",
}

func (ns Namespace) argPrefix(arg ArgInfo, t TypeInfo) string {
	// no prefix needed
	if n, ok := basicNames[t.Tag]; ok {
		return fmt.Sprintf("\treal_%s := %s(%s)\n", arg.Name, n, arg.Name)
	}
	switch t.Tag {
	case TagUTF8String, TagFilename:
		s := fmt.Sprintf("\treal_%s := (*C.gchar)(unsafe.Pointer(C.CString(%s)))\n", arg.Name, arg.Name)
		s += fmt.Sprintf("\tdefer C.free(unsafe.Pointer(real_%s))\n", arg.Name)
		return s
	case TagArray:
		// TODO
	case TagInterface:
		ctype := ns.CName(t.Interface)
		return fmt.Sprintf("\treal_%s = (*C.%s)(unsafe.Pointer(%s.Native()))\n", arg.Name, ctype, arg.Name)
	case TagGList:
		// TODO
	case TagGSList:
		// TODO
	case TagGHashTable:
		// TODO
	case TagGError:
		return fmt.Sprintf("\tvar real_%s *C.GError = nil\n", arg.Name)
	default:
		panic(fmt.Errorf("unknown tag type %d in argPrefix()", t.Tag))
	}
	return "\t//TODO\n"
}

func (ns Namespace) argSuffix(arg ArgInfo, t TypeInfo) string {
	if t.Tag == TagGError {
		s := fmt.Sprintf("\tif real_%s != nil {\n", arg.Name)
		s += fmt.Sprintf("\t\tcmsg_%s := (*C.char)(unsafe.Pointer(real_%s.message))\n", arg.Name)
		s += fmt.Sprintf("\t\t%s = errors.New(C.GoString(cmsg_%s)\n", arg.Name, arg.Name)
		s += fmt.Sprintf("\t}\n")
		return s
	}
	return ""			// no extra cleanup needed
}

func (ns Namespace) wrap(method FunctionInfo, to ObjectInfo, isInterface bool, iface InterfaceInfo) string {
	s := "func "
	prefix := ""
	suffix := ""
	// method receivers aren't listed in the arguments; we have to fake it
	if method.IsMethod {
		// make a fake receiver
		receiver := ArgInfo{
			BaseInfo:		BaseInfo{
				Namespace:	ns.Name,
				Name:		"this",		// let's hope nothing uses this name
			},
		}
		rtype := TypeInfo{
			BaseInfo:		BaseInfo{
				Namespace:	ns.Name,
			},
			IsPointer:		true,
			Tag:			TagInterface,
			Interface:		to.BaseInfo,
		}
		itype := rtype
		if isInterface {
			itype.Interface = iface.BaseInfo
		}
		s += "("
		prefix += ns.argPrefix(receiver, itype)
		suffix = ns.argSuffix(receiver, itype) + suffix
		s += ns.ArgValueToGo(receiver, rtype, false)
		s += ") "
	}
	// disambiguate between constructors
	// a more Go-like way would be to insert the type name after the New but before anything else :/ conformal/gotk3 does it this way so meh
	if (method.Flags & FunctionIsConstructor) != 0 {
		s += ns.GoName(to)
	}
	s += ns.GoName(method) + "("
	for i := 0; i < len(method.Args); i++ {
		arg := ns.Args[method.Args[i]]
		prefix += ns.argPrefix(arg, ns.Types[arg.Type])
		suffix = ns.argSuffix(arg, ns.Types[arg.Type]) + suffix
		s += ns.ArgToGo(method.Args[i])
		s += ", "
	}
	s += ") "
	ret := ns.TypeToGo(method.ReturnType)
	if ret != "" {
		s += "(ret " + ret + ") "
	}
	s += "{\n"
	s += prefix
	s += "\t"
	if ret != "" {
		s += "ret = (" + ret + ")("
	}
	s += "C." + ns.CName(method) + "("
	if method.IsMethod {
		s += "real_this, "
	}
	for i := 0; i < len(method.Args); i++ {
		arg := ns.Args[method.Args[i]]
		s += "real_" + arg.Name + ", "
	}
	s += ")"
	if ret != "" {
		s += ")"
	}
	s += "\n"
	s += suffix
	if ret != "" {
		s += "\treturn ret\n"
	}
	s += "}"
	return s
}
<|endoftext|>


<commit_before>package docker

import (
	"context"
	"fmt"
	"github.com/ViBiOh/dashboard/auth"
	"github.com/ViBiOh/dashboard/jsonHttp"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"gopkg.in/yaml.v2"
	"log"
	"net/http"
	"regexp"
	"strings"
	"time"
)

const minMemory = 16777216
const maxMemory = 805306368
const httpPrefix = `http://`
const portSeparator = `:`
const defaultTag = `:latest`
const deploySuffix = `_deploy`
const networkMode = `traefik`
const traefikHeatlhCheckLabel = `traefik.backend.healthcheck.path`
const traefikPortLabel = `traefik.port`
const linkSeparator = `:`
const waitTime = 10 * time.Second
const maxHealthCheckTry = 8

var httpClient = http.Client{Timeout: 5 * time.Second}

var imageTag = regexp.MustCompile(`^\S*?:\S+$`)

type dockerComposeService struct {
	Image       string
	Command     []string
	Environment map[string]string
	Labels      map[string]string
	Links       []string
	Ports       []string
	ReadOnly    bool  `yaml:"read_only"`
	CPUShares   int64 `yaml:"cpu_shares"`
	MemoryLimit int64 `yaml:"mem_limit"`
}

type dockerCompose struct {
	Version  string
	Services map[string]dockerComposeService
}

type deployedService struct {
	ID   string
	Name string
}

func getConfig(service *dockerComposeService, user *auth.User, appName string) *container.Config {
	environments := make([]string, len(service.Environment))
	for key, value := range service.Environment {
		environments = append(environments, key+`=`+value)
	}

	if service.Labels == nil {
		service.Labels = make(map[string]string)
	}

	service.Labels[ownerLabel] = user.Username
	service.Labels[appLabel] = appName

	config := container.Config{
		Image:  service.Image,
		Labels: service.Labels,
		Env:    environments,
	}

	if len(service.Command) != 0 {
		config.Cmd = service.Command
	}

	return &config
}

func getHostConfig(service *dockerComposeService) *container.HostConfig {
	hostConfig := container.HostConfig{
		LogConfig: container.LogConfig{Type: `json-file`, Config: map[string]string{
			`max-size`: `50m`,
		}},
		NetworkMode:   networkMode,
		RestartPolicy: container.RestartPolicy{Name: `on-failure`, MaximumRetryCount: 5},
		Resources: container.Resources{
			CPUShares: 128,
			Memory:    minMemory,
		},
		SecurityOpt: []string{`no-new-privileges`},
	}

	if service.ReadOnly {
		hostConfig.ReadonlyRootfs = service.ReadOnly
	}

	if service.CPUShares != 0 {
		hostConfig.Resources.CPUShares = service.CPUShares
	}

	if service.MemoryLimit != 0 {
		if service.MemoryLimit <= maxMemory {
			hostConfig.Resources.Memory = service.MemoryLimit
		} else {
			hostConfig.Resources.Memory = maxMemory
		}
	}

	return &hostConfig
}

func getNetworkConfig(service *dockerComposeService, deployedServices *map[string]deployedService) *network.NetworkingConfig {
	traefikConfig := network.EndpointSettings{}

	for _, link := range service.Links {
		linkParts := strings.Split(link, linkSeparator)

		target := linkParts[0]
		if linkedService, ok := (*deployedServices)[target]; ok {
			target = getFinalName(linkedService.Name)
		}

		alias := linkParts[0]
		if len(linkParts) > 1 {
			alias = linkParts[1]
		}

		traefikConfig.Links = append(traefikConfig.Links, target+linkSeparator+alias)
	}

	return &network.NetworkingConfig{
		EndpointsConfig: map[string]*network.EndpointSettings{
			networkMode: &traefikConfig,
		},
	}
}

func pullImage(image string, user *auth.User) error {
	if !imageTag.MatchString(image) {
		image = image + defaultTag
	}

	log.Print(user.Username + ` starts pulling for ` + image)
	pull, err := docker.ImagePull(context.Background(), image, types.ImagePullOptions{})
	if err != nil {
		return fmt.Errorf(`Error while pulling image: %v`, err)
	}

	readBody(pull)
	log.Print(user.Username + ` ends pulling for ` + image)
	return nil
}

func healthCheckContainers(containers []*types.ContainerJSON) bool {
	healthCheckSuccess := make(map[string]bool)

	sleepDuration := waitTime

	for i := 0; i < maxHealthCheckTry; i++ {
		if i != 0 {
			time.Sleep(waitTime)
			sleepDuration = sleepDuration * 2
		}

		for _, container := range containers {
			if !healthCheckSuccess[container.ID] && healthCheckContainer(container) {
				healthCheckSuccess[container.ID] = true
			}
		}

		if len(healthCheckSuccess) != len(containers) {
			return true
		}
	}

	return false
}

func healthCheckContainer(container *types.ContainerJSON) bool {
	if container.Config.Labels[traefikHeatlhCheckLabel] != `` {
		log.Printf(`Checking health of container %s`, container.Name)

		request, err := http.NewRequest(`GET`, httpPrefix+container.NetworkSettings.Networks[networkMode].IPAddress+portSeparator+container.Config.Labels[traefikPortLabel]+container.Config.Labels[traefikHeatlhCheckLabel], nil)
		if err != nil {
			log.Printf(`Unable to prepare health check request for container %s : %v`, container.Name, err)
			return true
		}

		response, err := httpClient.Do(request)
		if err != nil {
			log.Printf(`Unable to health check for container %s : %v`, container.Name, err)
			return false
		}

		defer response.Body.Close()
		if response.StatusCode != http.StatusOK {
			log.Printf(`Health check failed for container %s : HTTP/%d`, container.Name, response.StatusCode)
			return false
		}
	}

	return true
}

func cleanContainers(containers *[]types.Container, user *auth.User) {
	for _, container := range *containers {
		log.Print(user.Username + ` stops ` + strings.Join(container.Names, `, `))
		stopContainer(container.ID)
		log.Print(user.Username + ` rm ` + strings.Join(container.Names, `, `))
		rmContainer(container.ID)
	}
}

func renameDeployedContainers(containers *map[string]deployedService) error {
	for _, service := range *containers {
		if err := docker.ContainerRename(context.Background(), service.ID, getFinalName(service.Name)); err != nil {
			return fmt.Errorf(`Error while renaming container %s: %v`, service.Name, err)
		}
	}

	return nil
}

func getServiceFullName(app string, service string) string {
	return app + `_` + service + deploySuffix
}

func getFinalName(serviceFullName string) string {
	return strings.TrimSuffix(serviceFullName, deploySuffix)
}

func deleteServices(appName []byte, services map[string]deployedService) {
	log.Printf(`Deleting container %s`, appName)
	for service, container := range services {
		if err := rmContainer(container.ID); err != nil {
			log.Printf(`Error while deleting container %s : %v`, service, err)
		}
	}
}

func startServices(appName []byte, services map[string]deployedService) {
	log.Printf(`Starting containers for app %s`, appName)
	for service, container := range services {
		if err := startContainer(container.ID); err != nil {
			log.Printf(`Error while starting container %s : %v`, service, err)
		}
	}
}

func inspectServices(services map[string]deployedService) []*types.ContainerJSON {
	containers := make([]*types.ContainerJSON, 0, len(services))

	for service, container := range services {
		infos, err := inspectContainer(container.ID)
		if err != nil {
			log.Printf(`Error while inspecting container %s : %v`, service, err)
		}

		containers = append(containers, &infos)
	}

	return containers
}

func createAppHandler(w http.ResponseWriter, user *auth.User, appName []byte, composeFile []byte) {
	if len(appName) == 0 || len(composeFile) == 0 {
		http.Error(w, `An application name and a compose file are required`, http.StatusBadRequest)
		return
	}

	compose := dockerCompose{}
	if err := yaml.Unmarshal(composeFile, &compose); err != nil {
		errorHandler(w, fmt.Errorf(`Error while unmarshalling compose file: %v`, err))
		return
	}

	appNameStr := string(appName)
	log.Print(user.Username + ` deploys ` + appNameStr)

	ownerContainers, err := listContainers(user, &appNameStr)
	if err != nil {
		errorHandler(w, err)
		return
	}

	deployedServices := make(map[string]deployedService)

	var creationError = false
	for serviceName, service := range compose.Services {
		if err := pullImage(service.Image, user); err != nil {
			errorHandler(w, err)
			return
		}

		serviceFullName := getServiceFullName(appNameStr, serviceName)
		log.Printf(`%s starts %s`, user.Username, serviceFullName)

		createdContainer, err := docker.ContainerCreate(context.Background(), getConfig(&service, user, appNameStr), getHostConfig(&service), getNetworkConfig(&service, &deployedServices), serviceFullName)
		if err != nil {
			errorHandler(w, fmt.Errorf(`Error while creating container: %v`, err))
			creationError = true
			break
		}

		deployedServices[serviceName] = deployedService{ID: createdContainer.ID, Name: serviceFullName}
	}

	if creationError {
		deleteServices(appName, deployedServices)
		return
	}

	startServices(appName, deployedServices)

	go func() {
		log.Printf(`Waiting for new containers to start...`)

		if healthCheckContainers(inspectServices(deployedServices)) {
			log.Printf(`Health check succeed for started app %s`, appName)
			cleanContainers(&ownerContainers, user)

			if err := renameDeployedContainers(&deployedServices); err != nil {
				log.Print(err)
			}
		} else {
			log.Printf(`Health check failed for started app %s`, appName)
			deleteServices(appName, deployedServices)
		}
	}()

	jsonHttp.ResponseJSON(w, results{deployedServices})
}
<commit_msg>Update compose.go<commit_after>package docker

import (
	"context"
	"fmt"
	"github.com/ViBiOh/dashboard/auth"
	"github.com/ViBiOh/dashboard/jsonHttp"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/network"
	"gopkg.in/yaml.v2"
	"log"
	"net/http"
	"regexp"
	"strings"
	"time"
)

const minMemory = 16777216
const maxMemory = 805306368
const httpPrefix = `http://`
const portSeparator = `:`
const defaultTag = `:latest`
const deploySuffix = `_deploy`
const networkMode = `traefik`
const traefikHeatlhCheckLabel = `traefik.backend.healthcheck.path`
const traefikPortLabel = `traefik.port`
const linkSeparator = `:`
const waitTime = 10 * time.Second
const maxHealthCheckTry = 8

var httpClient = http.Client{Timeout: 5 * time.Second}

var imageTag = regexp.MustCompile(`^\S*?:\S+$`)

type dockerComposeService struct {
	Image       string
	Command     []string
	Environment map[string]string
	Labels      map[string]string
	Links       []string
	Ports       []string
	ReadOnly    bool  `yaml:"read_only"`
	CPUShares   int64 `yaml:"cpu_shares"`
	MemoryLimit int64 `yaml:"mem_limit"`
}

type dockerCompose struct {
	Version  string
	Services map[string]dockerComposeService
}

type deployedService struct {
	ID   string
	Name string
}

func getConfig(service *dockerComposeService, user *auth.User, appName string) *container.Config {
	environments := make([]string, len(service.Environment))
	for key, value := range service.Environment {
		environments = append(environments, key+`=`+value)
	}

	if service.Labels == nil {
		service.Labels = make(map[string]string)
	}

	service.Labels[ownerLabel] = user.Username
	service.Labels[appLabel] = appName

	config := container.Config{
		Image:  service.Image,
		Labels: service.Labels,
		Env:    environments,
	}

	if len(service.Command) != 0 {
		config.Cmd = service.Command
	}

	return &config
}

func getHostConfig(service *dockerComposeService) *container.HostConfig {
	hostConfig := container.HostConfig{
		LogConfig: container.LogConfig{Type: `json-file`, Config: map[string]string{
			`max-size`: `50m`,
		}},
		NetworkMode:   networkMode,
		RestartPolicy: container.RestartPolicy{Name: `on-failure`, MaximumRetryCount: 5},
		Resources: container.Resources{
			CPUShares: 128,
			Memory:    minMemory,
		},
		SecurityOpt: []string{`no-new-privileges`},
	}

	if service.ReadOnly {
		hostConfig.ReadonlyRootfs = service.ReadOnly
	}

	if service.CPUShares != 0 {
		hostConfig.Resources.CPUShares = service.CPUShares
	}

	if service.MemoryLimit != 0 {
		if service.MemoryLimit <= maxMemory {
			hostConfig.Resources.Memory = service.MemoryLimit
		} else {
			hostConfig.Resources.Memory = maxMemory
		}
	}

	return &hostConfig
}

func getNetworkConfig(service *dockerComposeService, deployedServices *map[string]deployedService) *network.NetworkingConfig {
	traefikConfig := network.EndpointSettings{}

	for _, link := range service.Links {
		linkParts := strings.Split(link, linkSeparator)

		target := linkParts[0]
		if linkedService, ok := (*deployedServices)[target]; ok {
			target = getFinalName(linkedService.Name)
		}

		alias := linkParts[0]
		if len(linkParts) > 1 {
			alias = linkParts[1]
		}

		traefikConfig.Links = append(traefikConfig.Links, target+linkSeparator+alias)
	}

	return &network.NetworkingConfig{
		EndpointsConfig: map[string]*network.EndpointSettings{
			networkMode: &traefikConfig,
		},
	}
}

func pullImage(image string, user *auth.User) error {
	if !imageTag.MatchString(image) {
		image = image + defaultTag
	}

	log.Print(user.Username + ` starts pulling for ` + image)
	pull, err := docker.ImagePull(context.Background(), image, types.ImagePullOptions{})
	if err != nil {
		return fmt.Errorf(`Error while pulling image: %v`, err)
	}

	readBody(pull)
	log.Print(user.Username + ` ends pulling for ` + image)
	return nil
}

func healthCheckContainers(containers []*types.ContainerJSON) bool {
	healthCheckSuccess := make(map[string]bool)

	sleepDuration := waitTime

	for i := 0; i < maxHealthCheckTry; i++ {
		if i != 0 {
			time.Sleep(waitTime)
			sleepDuration = sleepDuration * 2
		}

		for _, container := range containers {
			if !healthCheckSuccess[container.ID] && healthCheckContainer(container) {
				healthCheckSuccess[container.ID] = true
			}
		}

		if len(healthCheckSuccess) == len(containers) {
			return true
		}
	}

	return false
}

func healthCheckContainer(container *types.ContainerJSON) bool {
	if container.Config.Labels[traefikHeatlhCheckLabel] != `` {
		log.Printf(`Checking health of container %s`, container.Name)

		request, err := http.NewRequest(`GET`, httpPrefix+container.NetworkSettings.Networks[networkMode].IPAddress+portSeparator+container.Config.Labels[traefikPortLabel]+container.Config.Labels[traefikHeatlhCheckLabel], nil)
		if err != nil {
			log.Printf(`Unable to prepare health check request for container %s : %v`, container.Name, err)
			return true
		}

		response, err := httpClient.Do(request)
		if err != nil {
			log.Printf(`Unable to health check for container %s : %v`, container.Name, err)
			return false
		}

		defer response.Body.Close()
		if response.StatusCode != http.StatusOK {
			log.Printf(`Health check failed for container %s : HTTP/%d`, container.Name, response.StatusCode)
			return false
		}
	}

	return true
}

func cleanContainers(containers *[]types.Container, user *auth.User) {
	for _, container := range *containers {
		log.Print(user.Username + ` stops ` + strings.Join(container.Names, `, `))
		stopContainer(container.ID)
		log.Print(user.Username + ` rm ` + strings.Join(container.Names, `, `))
		rmContainer(container.ID)
	}
}

func renameDeployedContainers(containers *map[string]deployedService) error {
	for _, service := range *containers {
		if err := docker.ContainerRename(context.Background(), service.ID, getFinalName(service.Name)); err != nil {
			return fmt.Errorf(`Error while renaming container %s: %v`, service.Name, err)
		}
	}

	return nil
}

func getServiceFullName(app string, service string) string {
	return app + `_` + service + deploySuffix
}

func getFinalName(serviceFullName string) string {
	return strings.TrimSuffix(serviceFullName, deploySuffix)
}

func deleteServices(appName []byte, services map[string]deployedService) {
	log.Printf(`Deleting container %s`, appName)
	for service, container := range services {
		if err := rmContainer(container.ID); err != nil {
			log.Printf(`Error while deleting container %s : %v`, service, err)
		}
	}
}

func startServices(appName []byte, services map[string]deployedService) {
	log.Printf(`Starting containers for app %s`, appName)
	for service, container := range services {
		if err := startContainer(container.ID); err != nil {
			log.Printf(`Error while starting container %s : %v`, service, err)
		}
	}
}

func inspectServices(services map[string]deployedService) []*types.ContainerJSON {
	containers := make([]*types.ContainerJSON, 0, len(services))

	for service, container := range services {
		infos, err := inspectContainer(container.ID)
		if err != nil {
			log.Printf(`Error while inspecting container %s : %v`, service, err)
		}

		containers = append(containers, &infos)
	}

	return containers
}

func createAppHandler(w http.ResponseWriter, user *auth.User, appName []byte, composeFile []byte) {
	if len(appName) == 0 || len(composeFile) == 0 {
		http.Error(w, `An application name and a compose file are required`, http.StatusBadRequest)
		return
	}

	compose := dockerCompose{}
	if err := yaml.Unmarshal(composeFile, &compose); err != nil {
		errorHandler(w, fmt.Errorf(`Error while unmarshalling compose file: %v`, err))
		return
	}

	appNameStr := string(appName)
	log.Print(user.Username + ` deploys ` + appNameStr)

	ownerContainers, err := listContainers(user, &appNameStr)
	if err != nil {
		errorHandler(w, err)
		return
	}

	deployedServices := make(map[string]deployedService)

	var creationError = false
	for serviceName, service := range compose.Services {
		if err := pullImage(service.Image, user); err != nil {
			errorHandler(w, err)
			return
		}

		serviceFullName := getServiceFullName(appNameStr, serviceName)
		log.Printf(`%s starts %s`, user.Username, serviceFullName)

		createdContainer, err := docker.ContainerCreate(context.Background(), getConfig(&service, user, appNameStr), getHostConfig(&service), getNetworkConfig(&service, &deployedServices), serviceFullName)
		if err != nil {
			errorHandler(w, fmt.Errorf(`Error while creating container: %v`, err))
			creationError = true
			break
		}

		deployedServices[serviceName] = deployedService{ID: createdContainer.ID, Name: serviceFullName}
	}

	if creationError {
		deleteServices(appName, deployedServices)
		return
	}

	startServices(appName, deployedServices)

	go func() {
		log.Printf(`Waiting for new containers to start...`)

		if healthCheckContainers(inspectServices(deployedServices)) {
			log.Printf(`Health check succeed for started app %s`, appName)
			cleanContainers(&ownerContainers, user)

			if err := renameDeployedContainers(&deployedServices); err != nil {
				log.Print(err)
			}
		} else {
			log.Printf(`Health check failed for started app %s`, appName)
			deleteServices(appName, deployedServices)
		}
	}()

	jsonHttp.ResponseJSON(w, results{deployedServices})
}
<|endoftext|>


<commit_before>package git

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	pathlib "path"
	"strings"

	"github.com/buckhx/pathutil"
)

var NOF = []string{}

type Repository struct {
	Path string
}

func (repo *Repository) Op(cmd string, flags []string, args ...string) (out string, err error) {
	flags = append(flags, "-C", repo.Path)
	return Operation(cmd, flags, args...)
}

func (repo *Repository) Init() (err error) {
	_, err = repo.Op("init", NOF)
	return
}

func (repo *Repository) Add(paths ...string) (err error) {
	_, err = repo.Op("add", NOF, paths...)
	return
}

func (repo *Repository) Checkout(args ...string) (err error) {
	_, err = repo.Op("checkout", NOF, args...)
	return
}

func (repo *Repository) Commit(msg string) (err error) {
	_, err = repo.Op("commit", NOF, "-am", "\""+msg+"\"")
	return
}

// Append these patterns to .git/info/exclude
func (repo *Repository) Exclude(patterns ...string) (err error) {
	excludef, err := os.OpenFile(pathlib.Join(repo.Path, ".git/info/exclude"), os.O_APPEND|os.O_WRONLY, 0600)
	defer excludef.Close()
	if err != nil {
		return
	}
	for _, pattern := range patterns {
		_, err = excludef.WriteString(pattern + "\n")
	}
	return
}

func (repo *Repository) Fetch(remote string) (err error) {
	_, err = repo.Op("fetch", NOF, remote)
	return
}

func (repo *Repository) Pull(remote string, branch string) (err error) {
	_, err = repo.Op("pull", NOF, remote, branch)
	return
}

func (repo *Repository) Push(remote string, branch string) (err error) {
	_, err = repo.Op("push", NOF, remote, branch)
	return
}

func (repo *Repository) SetRemote(remote string, url string) (err error) {
	_, err = repo.Op("remote", NOF, "add", remote, url)
	_, err = repo.Op("remote", NOF, "set-url", remote, url)
	return
}

func Operation(command string, flags []string, args ...string) (string, error) {
	var stderr, stdout bytes.Buffer
	args = append([]string{command}, args...)
	args = append(flags, args...)
	cmd := exec.Command("git", args...)
	cmd.Stderr = &stderr
	cmd.Stdout = &stdout
	err := cmd.Run()
	if err != nil {
		err = fmt.Errorf("%s\n%s\n", stderr.String(), strings.Join(append([]string{"git"}, args...), " "))
	}
	fmt.Println(strings.Join(append([]string{"git"}, args...), " "))
	fmt.Printf("\t" + strings.Replace(stdout.String(), "\n", "\n\t", -1))
	fmt.Println("\t" + strings.Replace(stderr.String(), "\n", "\n\t", -1))
	return stdout.String(), err
}

func IsRepository(path string) bool {
	return pathutil.Exists(pathlib.Join(path, ".git"))
}

func NewRepository(path string) (repo *Repository, err error) {
	if !pathutil.Exists(path) {
		err = fmt.Errorf("Cannot instantiate Repository because path doesn't exist at %q", path)
	} else {
		repo = &Repository{}
		repo.Path = path
	}
	return
}
<commit_msg>use work-tree/git-dir<commit_after>package git

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	pathlib "path"
	"strings"

	"github.com/buckhx/pathutil"
)

var NOF = []string{}

type Repository struct {
	Path string
}

func (repo *Repository) Op(cmd string, flags []string, args ...string) (out string, err error) {
	//flags = append(flags, "-C", repo.Path) incompatible with git < 1.8.5
	flags = append(flags, "--git-dir", pathlib.Join(repo.Path, ".git"), "--work-tree", repo.Path)
	return Operation(cmd, flags, args...)
}

func (repo *Repository) Init() (err error) {
	_, err = repo.Op("init", NOF)
	return
}

func (repo *Repository) Add(paths ...string) (err error) {
	_, err = repo.Op("add", NOF, paths...)
	return
}

func (repo *Repository) Checkout(args ...string) (err error) {
	_, err = repo.Op("checkout", NOF, args...)
	return
}

func (repo *Repository) Commit(msg string) (err error) {
	_, err = repo.Op("commit", NOF, "-am", "\""+msg+"\"")
	return
}

// Append these patterns to .git/info/exclude
func (repo *Repository) Exclude(patterns ...string) (err error) {
	excludef, err := os.OpenFile(pathlib.Join(repo.Path, ".git/info/exclude"), os.O_APPEND|os.O_WRONLY, 0600)
	defer excludef.Close()
	if err != nil {
		return
	}
	for _, pattern := range patterns {
		_, err = excludef.WriteString(pattern + "\n")
	}
	return
}

func (repo *Repository) Fetch(remote string) (err error) {
	_, err = repo.Op("fetch", NOF, remote)
	return
}

func (repo *Repository) Pull(remote string, branch string) (err error) {
	_, err = repo.Op("pull", NOF, remote, branch)
	return
}

func (repo *Repository) Push(remote string, branch string) (err error) {
	_, err = repo.Op("push", NOF, remote, branch)
	return
}

func (repo *Repository) SetRemote(remote string, url string) (err error) {
	_, err = repo.Op("remote", NOF, "add", remote, url)
	_, err = repo.Op("remote", NOF, "set-url", remote, url)
	return
}

func Operation(command string, flags []string, args ...string) (string, error) {
	var stderr, stdout bytes.Buffer
	args = append([]string{command}, args...)
	args = append(flags, args...)
	cmd := exec.Command("git", args...)
	cmd.Stderr = &stderr
	cmd.Stdout = &stdout
	err := cmd.Run()
	if err != nil {
		err = fmt.Errorf("%s\n%s\n", stderr.String(), strings.Join(append([]string{"git"}, args...), " "))
	}
	fmt.Println(strings.Join(append([]string{"git"}, args...), " "))
	fmt.Printf("\t" + strings.Replace(stdout.String(), "\n", "\n\t", -1))
	fmt.Println("\t" + strings.Replace(stderr.String(), "\n", "\n\t", -1))
	return stdout.String(), err
}

func IsRepository(path string) bool {
	return pathutil.Exists(pathlib.Join(path, ".git"))
}

func NewRepository(path string) (repo *Repository, err error) {
	if !pathutil.Exists(path) {
		err = fmt.Errorf("Cannot instantiate Repository because path doesn't exist at %q", path)
	} else {
		repo = &Repository{}
		repo.Path = path
	}
	return
}
<|endoftext|>


<commit_before>package log

import (
	"reflect"
	"fmt"
)

const TIME_FORMAT = "2006-01-02T15:04:05.000000"
const LEVEL_DEBUG = "DEBUG"
const LEVEL_INFO  = "INFO"

type Config struct {
	Level string
	Formatter func(level string, message string, tags map[string]string, dateFormat string) string
	Output func(formattedMessage string)
	Program string
	DateFormat string
	Tags map[string]string
}

type LogFormattingFailed string
func (err LogFormattingFailed) String() string {
	return "LogFormattingFailed"
}

type InvalidContext string
func (err InvalidContext) Error() string {
	return "invalid type for context. Must be map, struct, ptr(map) or ptr(struct)"
}

type Logger interface {
	Info(string, interface{}) error
	Debug(string, interface{}) error
	ChildLogger(string, interface{}) (Logger, error)
}

type Log struct {
	config *Config
}
func (log *Log) Info(message string, tags interface{}) error {
	mergedTags, err := mergeTags(log.config.Tags, tags)
	if err != nil {
		return err
	}
	log.config.Output(log.config.Formatter(LEVEL_INFO, message, mergedTags, log.config.DateFormat))
	return nil
}

func (log *Log) Debug(message string, tags interface{}) error {
	if log.config.Level == LEVEL_DEBUG {
		mergedTags, err := mergeTags(log.config.Tags, tags)
		if err != nil {
			return err
		}
		log.config.Output(log.config.Formatter(LEVEL_DEBUG, message, mergedTags, log.config.DateFormat))
	}
	return nil
}

func (log *Log) ChildLogger(function string, context interface{}) (Logger, error) {
	childConfig := new(Config)
	childConfig.Level = log.config.Level
	childConfig.Formatter = log.config.Formatter
	childConfig.Output = log.config.Output
	childConfig.DateFormat = log.config.DateFormat
	childConfig.Program = log.config.Program
	childConfig.Tags = log.config.Tags
	childConfig.Tags["function"] = function
	mergedTags, err := mergeTags(log.config.Tags, context)
	if err != nil {
		return nil, err
	}
	childConfig.Tags = mergedTags
	return NewLogger(childConfig), nil

}

func NewLogger(config *Config) Logger {
	if config.Tags == nil {
		config.Tags = map[string]string{}
	}
	config.Tags["program"] = config.Program
	config.Tags["function"] = "main"
	logger := new(Log)
	logger.config = config
	return logger
}

func mergeTags(tags map[string]string, context interface{}) (map[string]string, error) {
	outputTags := map[string]string{}
	for name, value := range tags {
		outputTags[name] = value
	}

	if context == nil {
		return outputTags, nil
	}

	switch aTags := context.(type) {
	case error:
		outputTags["error"] = aTags.Error()
		return outputTags, nil
	case fmt.Stringer:
		outputTags["context"] = aTags.String()
		return outputTags, nil
	}

	reflectedValue := reflect.ValueOf(context)
	for {
		if reflectedValue.Kind() == reflect.Ptr {
			reflectedValue = reflectedValue.Elem()
		} else {
			break
		}
	}

	if reflectedValue.Kind() == reflect.Map {
		for _, name := range reflectedValue.MapKeys() {
			outputTags[name.String()] = reflectedValue.MapIndex(name).String()
		}
	} else if reflectedValue.Kind() == reflect.Struct {
		reflectedContext := reflect.TypeOf(context)
		for {
			if reflectedContext.Kind() == reflect.Ptr {
				reflectedContext = reflectedContext.Elem()
			} else {
				break
			}
		}

		for i := 0; i < reflectedContext.NumField(); i++ {
			currentField := reflectedContext.Field(i)
			if currentField.PkgPath == "" {  // merge only exported fields
				outputTags[currentField.Name] = reflectedValue.FieldByName(currentField.Name).String()
			}
		}
	} else {
		return nil, new(InvalidContext)
	}

	return outputTags, nil
}
<commit_msg>remove pointer receivers on Log<commit_after>package log

import (
	"reflect"
	"fmt"
)

const TIME_FORMAT = "2006-01-02T15:04:05.000000"
const LEVEL_DEBUG = "DEBUG"
const LEVEL_INFO  = "INFO"

type Config struct {
	Level string
	Formatter func(level string, message string, tags map[string]string, dateFormat string) string
	Output func(formattedMessage string)
	Program string
	DateFormat string
	Tags map[string]string
}

type LogFormattingFailed string
func (err LogFormattingFailed) String() string {
	return "LogFormattingFailed"
}

type InvalidContext string
func (err InvalidContext) Error() string {
	return "invalid type for context. Must be map, struct, ptr(map) or ptr(struct)"
}

type Logger interface {
	Info(string, interface{}) error
	Debug(string, interface{}) error
	ChildLogger(string, interface{}) (Logger, error)
}

type Log struct {
	config *Config
}
func (log Log) Info(message string, tags interface{}) error {
	mergedTags, err := mergeTags(log.config.Tags, tags)
	if err != nil {
		return err
	}
	log.config.Output(log.config.Formatter(LEVEL_INFO, message, mergedTags, log.config.DateFormat))
	return nil
}

func (log Log) Debug(message string, tags interface{}) error {
	if log.config.Level == LEVEL_DEBUG {
		mergedTags, err := mergeTags(log.config.Tags, tags)
		if err != nil {
			return err
		}
		log.config.Output(log.config.Formatter(LEVEL_DEBUG, message, mergedTags, log.config.DateFormat))
	}
	return nil
}

func (log Log) ChildLogger(function string, context interface{}) (Logger, error) {
	childConfig := new(Config)
	childConfig.Level = log.config.Level
	childConfig.Formatter = log.config.Formatter
	childConfig.Output = log.config.Output
	childConfig.DateFormat = log.config.DateFormat
	childConfig.Program = log.config.Program
	childConfig.Tags = log.config.Tags
	childConfig.Tags["function"] = function
	mergedTags, err := mergeTags(log.config.Tags, context)
	if err != nil {
		return nil, err
	}
	childConfig.Tags = mergedTags
	return NewLogger(childConfig), nil

}

func NewLogger(config *Config) Logger {
	if config.Tags == nil {
		config.Tags = map[string]string{}
	}
	config.Tags["program"] = config.Program
	config.Tags["function"] = "main"
	logger := new(Log)
	logger.config = config
	return logger
}

func mergeTags(tags map[string]string, context interface{}) (map[string]string, error) {
	outputTags := map[string]string{}
	for name, value := range tags {
		outputTags[name] = value
	}

	if context == nil {
		return outputTags, nil
	}

	switch aTags := context.(type) {
	case error:
		outputTags["error"] = aTags.Error()
		return outputTags, nil
	case fmt.Stringer:
		outputTags["context"] = aTags.String()
		return outputTags, nil
	}

	reflectedValue := reflect.ValueOf(context)
	for {
		if reflectedValue.Kind() == reflect.Ptr {
			reflectedValue = reflectedValue.Elem()
		} else {
			break
		}
	}

	if reflectedValue.Kind() == reflect.Map {
		for _, name := range reflectedValue.MapKeys() {
			outputTags[name.String()] = reflectedValue.MapIndex(name).String()
		}
	} else if reflectedValue.Kind() == reflect.Struct {
		reflectedContext := reflect.TypeOf(context)
		for {
			if reflectedContext.Kind() == reflect.Ptr {
				reflectedContext = reflectedContext.Elem()
			} else {
				break
			}
		}

		for i := 0; i < reflectedContext.NumField(); i++ {
			currentField := reflectedContext.Field(i)
			if currentField.PkgPath == "" {  // merge only exported fields
				outputTags[currentField.Name] = reflectedValue.FieldByName(currentField.Name).String()
			}
		}
	} else {
		return nil, new(InvalidContext)
	}

	return outputTags, nil
}
<|endoftext|>