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

// +build windows

package ansicolor

import (
	"bytes"
	"io"
	"strings"
	"syscall"
	"unsafe"
)

type csiState int

const (
	outsideCsiCode csiState = iota
	firstCsiCode
	secondCsiCode
)

type ansiColorWriter struct {
	w             io.Writer
	mode          outputMode
	state         csiState
	paramStartBuf bytes.Buffer
	paramBuf      bytes.Buffer
}

const (
	firstCsiChar   byte = '\x1b'
	secondeCsiChar byte = '['
	separatorChar  byte = ';'
	sgrCode        byte = 'm'
)

const (
	foregroundBlue      = uint16(0x0001)
	foregroundGreen     = uint16(0x0002)
	foregroundRed       = uint16(0x0004)
	foregroundIntensity = uint16(0x0008)
	backgroundBlue      = uint16(0x0010)
	backgroundGreen     = uint16(0x0020)
	backgroundRed       = uint16(0x0040)
	backgroundIntensity = uint16(0x0080)
	underscore          = uint16(0x8000)

	foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
	backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
)

const (
	ansiReset        = "0"
	ansiIntensityOn  = "1"
	ansiIntensityOff = "21"
	ansiUnderlineOn  = "4"
	ansiUnderlineOff = "24"
	ansiBlinkOn      = "5"
	ansiBlinkOff     = "25"

	ansiForegroundBlack   = "30"
	ansiForegroundRed     = "31"
	ansiForegroundGreen   = "32"
	ansiForegroundYellow  = "33"
	ansiForegroundBlue    = "34"
	ansiForegroundMagenta = "35"
	ansiForegroundCyan    = "36"
	ansiForegroundWhite   = "37"
	ansiForegroundDefault = "39"

	ansiBackgroundBlack   = "40"
	ansiBackgroundRed     = "41"
	ansiBackgroundGreen   = "42"
	ansiBackgroundYellow  = "43"
	ansiBackgroundBlue    = "44"
	ansiBackgroundMagenta = "45"
	ansiBackgroundCyan    = "46"
	ansiBackgroundWhite   = "47"
	ansiBackgroundDefault = "49"

	ansiLightForegroundGray    = "90"
	ansiLightForegroundRed     = "91"
	ansiLightForegroundGreen   = "92"
	ansiLightForegroundYellow  = "93"
	ansiLightForegroundBlue    = "94"
	ansiLightForegroundMagenta = "95"
	ansiLightForegroundCyan    = "96"
	ansiLightForegroundWhite   = "97"

	ansiLightBackgroundGray    = "100"
	ansiLightBackgroundRed     = "101"
	ansiLightBackgroundGreen   = "102"
	ansiLightBackgroundYellow  = "103"
	ansiLightBackgroundBlue    = "104"
	ansiLightBackgroundMagenta = "105"
	ansiLightBackgroundCyan    = "106"
	ansiLightBackgroundWhite   = "107"
)

type drawType int

const (
	foreground drawType = iota
	background
)

type winColor struct {
	code     uint16
	drawType drawType
}

var colorMap = map[string]winColor{
	ansiForegroundBlack:   {0, foreground},
	ansiForegroundRed:     {foregroundRed, foreground},
	ansiForegroundGreen:   {foregroundGreen, foreground},
	ansiForegroundYellow:  {foregroundRed | foregroundGreen, foreground},
	ansiForegroundBlue:    {foregroundBlue, foreground},
	ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
	ansiForegroundCyan:    {foregroundGreen | foregroundBlue, foreground},
	ansiForegroundWhite:   {foregroundRed | foregroundGreen | foregroundBlue, foreground},
	ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},

	ansiBackgroundBlack:   {0, background},
	ansiBackgroundRed:     {backgroundRed, background},
	ansiBackgroundGreen:   {backgroundGreen, background},
	ansiBackgroundYellow:  {backgroundRed | backgroundGreen, background},
	ansiBackgroundBlue:    {backgroundBlue, background},
	ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
	ansiBackgroundCyan:    {backgroundGreen | backgroundBlue, background},
	ansiBackgroundWhite:   {backgroundRed | backgroundGreen | backgroundBlue, background},
	ansiBackgroundDefault: {0, background},

	ansiLightForegroundGray:    {foregroundIntensity, foreground},
	ansiLightForegroundRed:     {foregroundIntensity | foregroundRed, foreground},
	ansiLightForegroundGreen:   {foregroundIntensity | foregroundGreen, foreground},
	ansiLightForegroundYellow:  {foregroundIntensity | foregroundRed | foregroundGreen, foreground},
	ansiLightForegroundBlue:    {foregroundIntensity | foregroundBlue, foreground},
	ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground},
	ansiLightForegroundCyan:    {foregroundIntensity | foregroundGreen | foregroundBlue, foreground},
	ansiLightForegroundWhite:   {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground},

	ansiLightBackgroundGray:    {backgroundIntensity, background},
	ansiLightBackgroundRed:     {backgroundIntensity | backgroundRed, background},
	ansiLightBackgroundGreen:   {backgroundIntensity | backgroundGreen, background},
	ansiLightBackgroundYellow:  {backgroundIntensity | backgroundRed | backgroundGreen, background},
	ansiLightBackgroundBlue:    {backgroundIntensity | backgroundBlue, background},
	ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background},
	ansiLightBackgroundCyan:    {backgroundIntensity | backgroundGreen | backgroundBlue, background},
	ansiLightBackgroundWhite:   {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background},
}

var (
	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
	procSetConsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
	defaultAttr                    *textAttributes
)

func init() {
	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
	if screenInfo != nil {
		colorMap[ansiForegroundDefault] = winColor{
			screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue),
			foreground,
		}
		colorMap[ansiBackgroundDefault] = winColor{
			screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue),
			background,
		}
		defaultAttr = convertTextAttr(screenInfo.WAttributes)
	}
}

type coord struct {
	X, Y int16
}

type smallRect struct {
	Left, Top, Right, Bottom int16
}

type consoleScreenBufferInfo struct {
	DwSize              coord
	DwCursorPosition    coord
	WAttributes         uint16
	SrWindow            smallRect
	DwMaximumWindowSize coord
}

func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
	var csbi consoleScreenBufferInfo
	ret, _, _ := procGetConsoleScreenBufferInfo.Call(
		hConsoleOutput,
		uintptr(unsafe.Pointer(&csbi)))
	if ret == 0 {
		return nil
	}
	return &csbi
}

func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
	ret, _, _ := procSetConsoleTextAttribute.Call(
		hConsoleOutput,
		uintptr(wAttributes))
	return ret != 0
}

type textAttributes struct {
	foregroundColor     uint16
	backgroundColor     uint16
	foregroundIntensity uint16
	backgroundIntensity uint16
	underscore          uint16
	otherAttributes     uint16
}

func convertTextAttr(winAttr uint16) *textAttributes {
	fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue)
	bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue)
	fgIntensity := winAttr & foregroundIntensity
	bgIntensity := winAttr & backgroundIntensity
	underline := winAttr & underscore
	otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore)
	return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes}
}

func convertWinAttr(textAttr *textAttributes) uint16 {
	var winAttr uint16 = 0
	winAttr |= textAttr.foregroundColor
	winAttr |= textAttr.backgroundColor
	winAttr |= textAttr.foregroundIntensity
	winAttr |= textAttr.backgroundIntensity
	winAttr |= textAttr.underscore
	winAttr |= textAttr.otherAttributes
	return winAttr
}

func changeColor(param []byte) bool {
	if defaultAttr == nil {
		return false
	}

	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
	if screenInfo == nil {
		return true
	}

	winAttr := convertTextAttr(screenInfo.WAttributes)
	strParam := string(param)
	if len(strParam) <= 0 {
		strParam = "0"
	}
	csiParam := strings.Split(strParam, string(separatorChar))
	for _, p := range csiParam {
		c, ok := colorMap[p]
		switch {
		case !ok:
			switch p {
			case ansiReset:
				winAttr.foregroundColor = defaultAttr.foregroundColor
				winAttr.backgroundColor = defaultAttr.backgroundColor
				winAttr.foregroundIntensity = defaultAttr.foregroundIntensity
				winAttr.backgroundIntensity = defaultAttr.backgroundIntensity
				winAttr.underscore = 0
				winAttr.otherAttributes = 0
			case ansiIntensityOn:
				winAttr.foregroundIntensity = foregroundIntensity
			case ansiIntensityOff:
				winAttr.foregroundIntensity = 0
			case ansiUnderlineOn:
				winAttr.underscore = underscore
			case ansiUnderlineOff:
				winAttr.underscore = 0
			case ansiBlinkOn:
				winAttr.backgroundIntensity = backgroundIntensity
			case ansiBlinkOff:
				winAttr.backgroundIntensity = 0
			default:
				// unknown code
			}
		case c.drawType == foreground:
			winAttr.foregroundColor = c.code
		case c.drawType == background:
			winAttr.backgroundColor = c.code
		}
	}
	winTextAttribute := convertWinAttr(winAttr)
	setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute)

	return true
}

func parseEscapeSequence(command byte, param []byte) bool {
	switch command {
	case sgrCode:
		return changeColor(param)
	default:
		return false
	}
}

func (cw *ansiColorWriter) flushBuffer() (int, error) {
	return cw.flushTo(cw.w)
}

func (cw *ansiColorWriter) resetBuffer() (int, error) {
	return cw.flushTo(nil)
}

func (cw *ansiColorWriter) flushTo(w io.Writer) (int, error) {
	var n1, n2 int
	var err error

	startBytes := cw.paramStartBuf.Bytes()
	cw.paramStartBuf.Reset()
	if w != nil {
		n1, err = cw.w.Write(startBytes)
		if err != nil {
			return n1, err
		}
	} else {
		n1 = len(startBytes)
	}
	paramBytes := cw.paramBuf.Bytes()
	cw.paramBuf.Reset()
	if w != nil {
		n2, err = cw.w.Write(paramBytes)
		if err != nil {
			return n1 + n2, err
		}
	} else {
		n2 = len(paramBytes)
	}
	return n1 + n2, nil
}

func isParameterChar(b byte) bool {
	return ('0' <= b && b <= '9') || b == separatorChar
}

func (cw *ansiColorWriter) Write(p []byte) (int, error) {
	r, nw, first, last := 0, 0, 0, 0
	if cw.mode != DiscardNonColorEscSeq {
		cw.state = outsideCsiCode
		cw.resetBuffer()
	}

	var err error
	for i, ch := range p {
		switch cw.state {
		case outsideCsiCode:
			if ch == firstCsiChar {
				cw.paramStartBuf.WriteByte(ch)
				cw.state = firstCsiCode
			}
		case firstCsiCode:
			switch ch {
			case firstCsiChar:
				cw.paramStartBuf.WriteByte(ch)
				break
			case secondeCsiChar:
				cw.paramStartBuf.WriteByte(ch)
				cw.state = secondCsiCode
				last = i - 1
			default:
				cw.resetBuffer()
				cw.state = outsideCsiCode
			}
		case secondCsiCode:
			if isParameterChar(ch) {
				cw.paramBuf.WriteByte(ch)
			} else {
				nw, err = cw.w.Write(p[first:last])
				r += nw
				if err != nil {
					return r, err
				}
				first = i + 1
				if !parseEscapeSequence(ch, cw.paramBuf.Bytes()) {
					if cw.mode == OutputNonColorEscSeq {
						cw.paramBuf.WriteByte(ch)
						nw, err := cw.flushBuffer()
						if err != nil {
							return r, err
						}
						r += nw
					}
				} else {
					n, _ := cw.resetBuffer()
					// Add one more to the size of the buffer for the last ch
					r += n + 1
				}

				cw.state = outsideCsiCode
			}
		default:
			cw.state = outsideCsiCode
		}
	}

	if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode {
		nw, err = cw.w.Write(p[first:len(p)])
		r += nw
	}

	return r, err
}
<commit_msg>Improved output when there is no console<commit_after>// Copyright 2014 shiena Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// +build windows

package ansicolor

import (
	"bytes"
	"io"
	"strings"
	"syscall"
	"unsafe"
)

type csiState int

const (
	outsideCsiCode csiState = iota
	firstCsiCode
	secondCsiCode
)

type parseResult int

const (
	noConsole parseResult = iota
	changedColor
	unknown
)

type ansiColorWriter struct {
	w             io.Writer
	mode          outputMode
	state         csiState
	paramStartBuf bytes.Buffer
	paramBuf      bytes.Buffer
}

const (
	firstCsiChar   byte = '\x1b'
	secondeCsiChar byte = '['
	separatorChar  byte = ';'
	sgrCode        byte = 'm'
)

const (
	foregroundBlue      = uint16(0x0001)
	foregroundGreen     = uint16(0x0002)
	foregroundRed       = uint16(0x0004)
	foregroundIntensity = uint16(0x0008)
	backgroundBlue      = uint16(0x0010)
	backgroundGreen     = uint16(0x0020)
	backgroundRed       = uint16(0x0040)
	backgroundIntensity = uint16(0x0080)
	underscore          = uint16(0x8000)

	foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
	backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
)

const (
	ansiReset        = "0"
	ansiIntensityOn  = "1"
	ansiIntensityOff = "21"
	ansiUnderlineOn  = "4"
	ansiUnderlineOff = "24"
	ansiBlinkOn      = "5"
	ansiBlinkOff     = "25"

	ansiForegroundBlack   = "30"
	ansiForegroundRed     = "31"
	ansiForegroundGreen   = "32"
	ansiForegroundYellow  = "33"
	ansiForegroundBlue    = "34"
	ansiForegroundMagenta = "35"
	ansiForegroundCyan    = "36"
	ansiForegroundWhite   = "37"
	ansiForegroundDefault = "39"

	ansiBackgroundBlack   = "40"
	ansiBackgroundRed     = "41"
	ansiBackgroundGreen   = "42"
	ansiBackgroundYellow  = "43"
	ansiBackgroundBlue    = "44"
	ansiBackgroundMagenta = "45"
	ansiBackgroundCyan    = "46"
	ansiBackgroundWhite   = "47"
	ansiBackgroundDefault = "49"

	ansiLightForegroundGray    = "90"
	ansiLightForegroundRed     = "91"
	ansiLightForegroundGreen   = "92"
	ansiLightForegroundYellow  = "93"
	ansiLightForegroundBlue    = "94"
	ansiLightForegroundMagenta = "95"
	ansiLightForegroundCyan    = "96"
	ansiLightForegroundWhite   = "97"

	ansiLightBackgroundGray    = "100"
	ansiLightBackgroundRed     = "101"
	ansiLightBackgroundGreen   = "102"
	ansiLightBackgroundYellow  = "103"
	ansiLightBackgroundBlue    = "104"
	ansiLightBackgroundMagenta = "105"
	ansiLightBackgroundCyan    = "106"
	ansiLightBackgroundWhite   = "107"
)

type drawType int

const (
	foreground drawType = iota
	background
)

type winColor struct {
	code     uint16
	drawType drawType
}

var colorMap = map[string]winColor{
	ansiForegroundBlack:   {0, foreground},
	ansiForegroundRed:     {foregroundRed, foreground},
	ansiForegroundGreen:   {foregroundGreen, foreground},
	ansiForegroundYellow:  {foregroundRed | foregroundGreen, foreground},
	ansiForegroundBlue:    {foregroundBlue, foreground},
	ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
	ansiForegroundCyan:    {foregroundGreen | foregroundBlue, foreground},
	ansiForegroundWhite:   {foregroundRed | foregroundGreen | foregroundBlue, foreground},
	ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},

	ansiBackgroundBlack:   {0, background},
	ansiBackgroundRed:     {backgroundRed, background},
	ansiBackgroundGreen:   {backgroundGreen, background},
	ansiBackgroundYellow:  {backgroundRed | backgroundGreen, background},
	ansiBackgroundBlue:    {backgroundBlue, background},
	ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
	ansiBackgroundCyan:    {backgroundGreen | backgroundBlue, background},
	ansiBackgroundWhite:   {backgroundRed | backgroundGreen | backgroundBlue, background},
	ansiBackgroundDefault: {0, background},

	ansiLightForegroundGray:    {foregroundIntensity, foreground},
	ansiLightForegroundRed:     {foregroundIntensity | foregroundRed, foreground},
	ansiLightForegroundGreen:   {foregroundIntensity | foregroundGreen, foreground},
	ansiLightForegroundYellow:  {foregroundIntensity | foregroundRed | foregroundGreen, foreground},
	ansiLightForegroundBlue:    {foregroundIntensity | foregroundBlue, foreground},
	ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground},
	ansiLightForegroundCyan:    {foregroundIntensity | foregroundGreen | foregroundBlue, foreground},
	ansiLightForegroundWhite:   {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground},

	ansiLightBackgroundGray:    {backgroundIntensity, background},
	ansiLightBackgroundRed:     {backgroundIntensity | backgroundRed, background},
	ansiLightBackgroundGreen:   {backgroundIntensity | backgroundGreen, background},
	ansiLightBackgroundYellow:  {backgroundIntensity | backgroundRed | backgroundGreen, background},
	ansiLightBackgroundBlue:    {backgroundIntensity | backgroundBlue, background},
	ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background},
	ansiLightBackgroundCyan:    {backgroundIntensity | backgroundGreen | backgroundBlue, background},
	ansiLightBackgroundWhite:   {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background},
}

var (
	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
	procSetConsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
	defaultAttr                    *textAttributes
)

func init() {
	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
	if screenInfo != nil {
		colorMap[ansiForegroundDefault] = winColor{
			screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue),
			foreground,
		}
		colorMap[ansiBackgroundDefault] = winColor{
			screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue),
			background,
		}
		defaultAttr = convertTextAttr(screenInfo.WAttributes)
	}
}

type coord struct {
	X, Y int16
}

type smallRect struct {
	Left, Top, Right, Bottom int16
}

type consoleScreenBufferInfo struct {
	DwSize              coord
	DwCursorPosition    coord
	WAttributes         uint16
	SrWindow            smallRect
	DwMaximumWindowSize coord
}

func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
	var csbi consoleScreenBufferInfo
	ret, _, _ := procGetConsoleScreenBufferInfo.Call(
		hConsoleOutput,
		uintptr(unsafe.Pointer(&csbi)))
	if ret == 0 {
		return nil
	}
	return &csbi
}

func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
	ret, _, _ := procSetConsoleTextAttribute.Call(
		hConsoleOutput,
		uintptr(wAttributes))
	return ret != 0
}

type textAttributes struct {
	foregroundColor     uint16
	backgroundColor     uint16
	foregroundIntensity uint16
	backgroundIntensity uint16
	underscore          uint16
	otherAttributes     uint16
}

func convertTextAttr(winAttr uint16) *textAttributes {
	fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue)
	bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue)
	fgIntensity := winAttr & foregroundIntensity
	bgIntensity := winAttr & backgroundIntensity
	underline := winAttr & underscore
	otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore)
	return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes}
}

func convertWinAttr(textAttr *textAttributes) uint16 {
	var winAttr uint16 = 0
	winAttr |= textAttr.foregroundColor
	winAttr |= textAttr.backgroundColor
	winAttr |= textAttr.foregroundIntensity
	winAttr |= textAttr.backgroundIntensity
	winAttr |= textAttr.underscore
	winAttr |= textAttr.otherAttributes
	return winAttr
}

func changeColor(param []byte) parseResult {
	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
	if screenInfo == nil {
		return noConsole
	}

	winAttr := convertTextAttr(screenInfo.WAttributes)
	strParam := string(param)
	if len(strParam) <= 0 {
		strParam = "0"
	}
	csiParam := strings.Split(strParam, string(separatorChar))
	for _, p := range csiParam {
		c, ok := colorMap[p]
		switch {
		case !ok:
			switch p {
			case ansiReset:
				winAttr.foregroundColor = defaultAttr.foregroundColor
				winAttr.backgroundColor = defaultAttr.backgroundColor
				winAttr.foregroundIntensity = defaultAttr.foregroundIntensity
				winAttr.backgroundIntensity = defaultAttr.backgroundIntensity
				winAttr.underscore = 0
				winAttr.otherAttributes = 0
			case ansiIntensityOn:
				winAttr.foregroundIntensity = foregroundIntensity
			case ansiIntensityOff:
				winAttr.foregroundIntensity = 0
			case ansiUnderlineOn:
				winAttr.underscore = underscore
			case ansiUnderlineOff:
				winAttr.underscore = 0
			case ansiBlinkOn:
				winAttr.backgroundIntensity = backgroundIntensity
			case ansiBlinkOff:
				winAttr.backgroundIntensity = 0
			default:
				// unknown code
			}
		case c.drawType == foreground:
			winAttr.foregroundColor = c.code
		case c.drawType == background:
			winAttr.backgroundColor = c.code
		}
	}
	winTextAttribute := convertWinAttr(winAttr)
	setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute)

	return changedColor
}

func parseEscapeSequence(command byte, param []byte) parseResult {
	if defaultAttr == nil {
		return noConsole
	}

	switch command {
	case sgrCode:
		return changeColor(param)
	default:
		return unknown
	}
}

func (cw *ansiColorWriter) flushBuffer() (int, error) {
	return cw.flushTo(cw.w)
}

func (cw *ansiColorWriter) resetBuffer() (int, error) {
	return cw.flushTo(nil)
}

func (cw *ansiColorWriter) flushTo(w io.Writer) (int, error) {
	var n1, n2 int
	var err error

	startBytes := cw.paramStartBuf.Bytes()
	cw.paramStartBuf.Reset()
	if w != nil {
		n1, err = cw.w.Write(startBytes)
		if err != nil {
			return n1, err
		}
	} else {
		n1 = len(startBytes)
	}
	paramBytes := cw.paramBuf.Bytes()
	cw.paramBuf.Reset()
	if w != nil {
		n2, err = cw.w.Write(paramBytes)
		if err != nil {
			return n1 + n2, err
		}
	} else {
		n2 = len(paramBytes)
	}
	return n1 + n2, nil
}

func isParameterChar(b byte) bool {
	return ('0' <= b && b <= '9') || b == separatorChar
}

func (cw *ansiColorWriter) Write(p []byte) (int, error) {
	r, nw, first, last := 0, 0, 0, 0
	if cw.mode != DiscardNonColorEscSeq {
		cw.state = outsideCsiCode
		cw.resetBuffer()
	}

	var err error
	for i, ch := range p {
		switch cw.state {
		case outsideCsiCode:
			if ch == firstCsiChar {
				cw.paramStartBuf.WriteByte(ch)
				cw.state = firstCsiCode
			}
		case firstCsiCode:
			switch ch {
			case firstCsiChar:
				cw.paramStartBuf.WriteByte(ch)
				break
			case secondeCsiChar:
				cw.paramStartBuf.WriteByte(ch)
				cw.state = secondCsiCode
				last = i - 1
			default:
				cw.resetBuffer()
				cw.state = outsideCsiCode
			}
		case secondCsiCode:
			if isParameterChar(ch) {
				cw.paramBuf.WriteByte(ch)
			} else {
				nw, err = cw.w.Write(p[first:last])
				r += nw
				if err != nil {
					return r, err
				}
				first = i + 1
				result := parseEscapeSequence(ch, cw.paramBuf.Bytes())
				if result == noConsole || (cw.mode == OutputNonColorEscSeq && result == unknown) {
					cw.paramBuf.WriteByte(ch)
					nw, err := cw.flushBuffer()
					if err != nil {
						return r, err
					}
					r += nw
				} else {
					n, _ := cw.resetBuffer()
					// Add one more to the size of the buffer for the last ch
					r += n + 1
				}

				cw.state = outsideCsiCode
			}
		default:
			cw.state = outsideCsiCode
		}
	}

	if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode {
		nw, err = cw.w.Write(p[first:len(p)])
		r += nw
	}

	return r, err
}
<|endoftext|>


<commit_before>// Copyright 2015 Dmitry Lagoza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package http

import (
	"time"
	"net"
)

type Pool struct {
	total, busy uint
	jobsChan chan *job
	startQuitChan chan bool
	fullStopChan chan bool
	busyFreeChan chan bool
}

type job struct {
	c *conn
	stop bool
}

// manager handles "start worker", "worker quit", "worker busy or free"
// and "stop all worker" requests
func (p *Pool) manager() {
	for {
		select {
			case c:= <-p.startQuitChan:
				if (c) {
					go p.worker()
					p.total++
				} else {
					p.total--
				}
			case b:= <-p.busyFreeChan:
				if (b) {
					p.busy++
				} else {
					p.busy--
				}
			case <- p.fullStopChan:
				if p.total > 0 {
					p.StopWorker()
					p.fullStopChan <- true
				}
		}
	}
}

// worker waits job then executes it
func (p *Pool) worker() {
	for {
		job := <-p.jobsChan
		if (job.stop) {
			p.startQuitChan<-false
			return
		}
		p.busyFreeChan<-true
		job.c.serve()
		p.busyFreeChan<-false
	}
}

// GetTotal get total count of workers in the pool
func (p *Pool) GetTotal() (uint) {
	return p.total
}

// GetFree get count of free workers in the pool
func (p *Pool) GetFree() (uint) {
	return p.total-p.busy
}

// GetBusy get count of busy workers in the pool
func (p *Pool) GetBusy() (uint) {
	return p.busy
}

// AddJob adds connection to the job's channel
func (p *Pool) AddJob(c *conn) {
	p.jobsChan<-&job{c: c, stop: false}
}

// StopWorker sends stop signal to a single worker
func (p *Pool) StopWorker() {
	p.jobsChan<-&job{c: nil, stop: true}
}

// StartWorker starts a single worker
func (p *Pool) StartWorker() {
	p.startQuitChan<-true
}

// StartWorkers starts a multiple workers
func (p *Pool) StartWorkers(workerCount uint) {
	var i uint
	for i=0; i<workerCount; i++ {
		p.StartWorker()
	}
}
// FullStop sends the stop signal to all workers in pool
func (p *Pool) FullStop() {
	p.fullStopChan <- true
}

// NewPool creates new pool instance, starts the pool manager and workers.
// For empty pool with pool manager only use workerCount with 0 value.
func NewPool(workerCount uint) (*Pool){
	pool:=&Pool{jobsChan: make(chan *job), startQuitChan: make(chan bool, 1), fullStopChan: make(chan bool, 1), busyFreeChan: make(chan bool)}
	go pool.manager()
	pool.StartWorkers(workerCount)
	return pool
}

// ListenAndServeWithPool listens on the TCP network address srv.Addr and then
// calls ServeWithPool to handle requests on incoming connections in pool.  If
// srv.Addr is blank, ":http" is used.
func (srv *Server) ListenAndServeWithPool(pool *Pool) error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.ServeWithPool(tcpKeepAliveListener{ln.(*net.TCPListener)}, pool)
}

// ListenAndServeWithPool listens on the TCP network address addr
// and then calls ServeWithPool with handler to handle requests
// on incoming connections in pool.  Handler is typically nil,
// in which case the DefaultServeMux is used.
//
// A trivial example server is:
//
//      package main
//
//      import (
//              "io"
//              "net/http"
//              "log"
//      )
//
//      // hello world, the web server
//      func HelloServer(w http.ResponseWriter, req *http.Request) {
//              io.WriteString(w, "hello, world!\n")
//      }
//
//      func main() {
//              http.HandleFunc("/hello", HelloServer)
//		p = http.NewPool(1000)
//              err := http.ListenAndServe(":12345", nil, p)
//              if err != nil {
//                      log.Fatal("ListenAndServe: ", err)
//              }
//      }
func ListenAndServeWithPool(addr string, handler Handler, pool *Pool) error {
        server := &Server{Addr: addr, Handler: handler}
        return server.ListenAndServeWithPool(pool)
}

// ServeWithPool accepts incoming connections on the Listener l, sends a job to pool
// for each.  The pool read requests and then call srv.Handler to reply to them.
func (srv *Server) ServeWithPool(l net.Listener, pool *Pool)error {
	defer l.Close()
	var tempDelay time.Duration // how long to sleep on accept failure
	for {
		rw, e := l.Accept()
		if e != nil {
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return e
		}
		tempDelay = 0
		c, err := srv.newConn(rw)
		if err != nil {
			continue
		}
		c.setState(c.rwc, StateNew) // before Serve can return
		pool.AddJob(c)
	}
}
<commit_msg>Code formatting<commit_after>// Copyright 2015 Dmitry Lagoza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package http

import (
	"net"
	"time"
)

type Pool struct {
	total, busy   uint
	jobsChan      chan *job
	startQuitChan chan bool
	fullStopChan  chan bool
	busyFreeChan  chan bool
}

type job struct {
	c    *conn
	stop bool
}

// manager handles "start worker", "worker quit", "worker busy or free"
// and "stop all worker" requests
func (p *Pool) manager() {
	for {
		select {
		case c := <-p.startQuitChan:
			if c {
				go p.worker()
				p.total++
			} else {
				p.total--
			}
		case b := <-p.busyFreeChan:
			if b {
				p.busy++
			} else {
				p.busy--
			}
		case <-p.fullStopChan:
			if p.total > 0 {
				p.StopWorker()
				p.fullStopChan <- true
			}
		}
	}
}

// worker waits job then executes it
func (p *Pool) worker() {
	for {
		job := <-p.jobsChan
		if job.stop {
			p.startQuitChan <- false
			return
		}
		p.busyFreeChan <- true
		job.c.serve()
		p.busyFreeChan <- false
	}
}

// GetTotal get total count of workers in the pool
func (p *Pool) GetTotal() uint {
	return p.total
}

// GetFree get count of free workers in the pool
func (p *Pool) GetFree() uint {
	return p.total - p.busy
}

// GetBusy get count of busy workers in the pool
func (p *Pool) GetBusy() uint {
	return p.busy
}

// AddJob adds connection to the job's channel
func (p *Pool) AddJob(c *conn) {
	p.jobsChan <- &job{c: c, stop: false}
}

// StopWorker sends stop signal to a single worker
func (p *Pool) StopWorker() {
	p.jobsChan <- &job{c: nil, stop: true}
}

// StartWorker starts a single worker
func (p *Pool) StartWorker() {
	p.startQuitChan <- true
}

// StartWorkers starts a multiple workers
func (p *Pool) StartWorkers(workerCount uint) {
	var i uint
	for i = 0; i < workerCount; i++ {
		p.StartWorker()
	}
}

// FullStop sends the stop signal to all workers in pool
func (p *Pool) FullStop() {
	p.fullStopChan <- true
}

// NewPool creates new pool instance, starts the pool manager and workers.
// For empty pool with pool manager only use workerCount with 0 value.
func NewPool(workerCount uint) *Pool {
	pool := &Pool{jobsChan: make(chan *job), startQuitChan: make(chan bool, 1), fullStopChan: make(chan bool, 1), busyFreeChan: make(chan bool)}
	go pool.manager()
	pool.StartWorkers(workerCount)
	return pool
}

// ListenAndServeWithPool listens on the TCP network address srv.Addr and then
// calls ServeWithPool to handle requests on incoming connections in pool.  If
// srv.Addr is blank, ":http" is used.
func (srv *Server) ListenAndServeWithPool(pool *Pool) error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	return srv.ServeWithPool(tcpKeepAliveListener{ln.(*net.TCPListener)}, pool)
}

// ListenAndServeWithPool listens on the TCP network address addr
// and then calls ServeWithPool with handler to handle requests
// on incoming connections in pool.  Handler is typically nil,
// in which case the DefaultServeMux is used.
//
// A trivial example server is:
//
//      package main
//
//      import (
//              "io"
//              "net/http"
//              "log"
//      )
//
//      // hello world, the web server
//      func HelloServer(w http.ResponseWriter, req *http.Request) {
//              io.WriteString(w, "hello, world!\n")
//      }
//
//      func main() {
//              http.HandleFunc("/hello", HelloServer)
//		p = http.NewPool(1000)
//              err := http.ListenAndServe(":12345", nil, p)
//              if err != nil {
//                      log.Fatal("ListenAndServe: ", err)
//              }
//      }
func ListenAndServeWithPool(addr string, handler Handler, pool *Pool) error {
	server := &Server{Addr: addr, Handler: handler}
	return server.ListenAndServeWithPool(pool)
}

// ServeWithPool accepts incoming connections on the Listener l, sends a job to pool
// for each.  The pool read requests and then call srv.Handler to reply to them.
func (srv *Server) ServeWithPool(l net.Listener, pool *Pool) error {
	defer l.Close()
	var tempDelay time.Duration // how long to sleep on accept failure
	for {
		rw, e := l.Accept()
		if e != nil {
			if ne, ok := e.(net.Error); ok && ne.Temporary() {
				if tempDelay == 0 {
					tempDelay = 5 * time.Millisecond
				} else {
					tempDelay *= 2
				}
				if max := 1 * time.Second; tempDelay > max {
					tempDelay = max
				}
				srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
				time.Sleep(tempDelay)
				continue
			}
			return e
		}
		tempDelay = 0
		c, err := srv.newConn(rw)
		if err != nil {
			continue
		}
		c.setState(c.rwc, StateNew) // before Serve can return
		pool.AddJob(c)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"crypto/sha1"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"time"
)

const BUFFSIZE = 4

func main() {
	if len(os.Args) < 2 {
		fmt.Printf("Usage: %s <cmd> [args...] [> <output file>]\n", os.Args[0])
		os.Exit(1)
	}

	subCmd := os.Args[1]
	subArgs := os.Args[2:]

	cmd := exec.Command(subCmd, subArgs...)

	cmd.Stderr = os.Stderr

	cmdReader, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Printf("Error getting stdout: %v", err)
		os.Exit(1)
	}

	hash := sha1.New()
	hashWriter := bufio.NewWriter(hash)

	go func() {
		inbuf := make([]byte, BUFFSIZE)
		bread := 0

		// Read stdout from subprocess into buffer inbuf.
		// Then write inbuf to both the hasher and to the output file.

		reader := bufio.NewReader(cmdReader)
		for ; err != io.EOF; bread, err = reader.Read(inbuf) {
			outbuf := bytes.NewReader(inbuf[:bread])
			if _, err = io.Copy(hashWriter, outbuf); err != nil {
				log.Fatal(err)
			}

			// Rewind so we can copy it back out to our stdout too.
			if _, err = outbuf.Seek(0, 0); err != nil {
				log.Fatal(err)
			}

			if _, err = io.Copy(os.Stdout, outbuf); err != nil {
				log.Fatal(err)
			}
		}
	}()

	// Execute cmd and wait for it to finish.
	err = cmd.Start()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error starting cmd: %s\n", err)
		os.Exit(1)
	}

	err = cmd.Wait()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error waiting for cmd: %v", err)
		os.Exit(1)
	}

	hashWriter.Flush()

	// Write the provenance record for later query by whence.

	pFile, err := os.OpenFile(filepath.Join(os.Getenv("HOME"), ".prov"), os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	defer pFile.Close()
	if err != nil {
		log.Fatalf("Couldn't open .prov: %v", err)
	}

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}

	line := []string{
		fmt.Sprintf("%x", hash.Sum(nil)),
		fmt.Sprintf("%v", time.Now()),
		os.Getenv("USER"),
		dir,
		fmt.Sprintf("%s", subCmd),
	}
	line = append(line, subArgs...)
	w := csv.NewWriter(pFile)

	err = w.Write(line)
	if err != nil {
		log.Fatalf("Couldn't write to .prov: %v", err)
	}
	w.Flush()
}
<commit_msg>fix buffer size<commit_after>package main

import (
	"bufio"
	"bytes"
	"crypto/sha1"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"time"
)

const BUFFSIZE = 1024

func main() {
	if len(os.Args) < 2 {
		fmt.Printf("Usage: %s <cmd> [args...] [> <output file>]\n", os.Args[0])
		os.Exit(1)
	}

	subCmd := os.Args[1]
	subArgs := os.Args[2:]

	cmd := exec.Command(subCmd, subArgs...)

	cmd.Stderr = os.Stderr

	cmdReader, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Printf("Error getting stdout: %v", err)
		os.Exit(1)
	}

	hash := sha1.New()
	hashWriter := bufio.NewWriter(hash)

	go func() {
		inbuf := make([]byte, BUFFSIZE)
		bread := 0

		// Read stdout from subprocess into buffer inbuf.
		// Then write inbuf to both the hasher and to the output file.

		reader := bufio.NewReader(cmdReader)
		for ; err != io.EOF; bread, err = reader.Read(inbuf) {
			outbuf := bytes.NewReader(inbuf[:bread])
			if _, err = io.Copy(hashWriter, outbuf); err != nil {
				log.Fatal(err)
			}

			// Rewind so we can copy it back out to our stdout too.
			if _, err = outbuf.Seek(0, 0); err != nil {
				log.Fatal(err)
			}

			if _, err = io.Copy(os.Stdout, outbuf); err != nil {
				log.Fatal(err)
			}
		}
	}()

	// Execute cmd and wait for it to finish.
	err = cmd.Start()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error starting cmd: %s\n", err)
		os.Exit(1)
	}

	err = cmd.Wait()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error waiting for cmd: %v", err)
		os.Exit(1)
	}

	hashWriter.Flush()

	// Write the provenance record for later query by whence.

	pFile, err := os.OpenFile(filepath.Join(os.Getenv("HOME"), ".prov"), os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	defer pFile.Close()
	if err != nil {
		log.Fatalf("Couldn't open .prov: %v", err)
	}

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}

	line := []string{
		fmt.Sprintf("%x", hash.Sum(nil)),
		fmt.Sprintf("%v", time.Now()),
		os.Getenv("USER"),
		dir,
		fmt.Sprintf("%s", subCmd),
	}
	line = append(line, subArgs...)
	w := csv.NewWriter(pFile)

	err = w.Write(line)
	if err != nil {
		log.Fatalf("Couldn't write to .prov: %v", err)
	}
	w.Flush()
}
<|endoftext|>


<commit_before>package main

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

var cmdPush = &Command{
	Run:   runPush,
	Usage: "push (<metadata> <name> | <file>...)",
	Short: "Deploy artifact from a local directory",
	Long: `
Deploy artifact from a local directory
<metadata>: Accepts either actual directory name or Metadata type

Examples:
  force push classes MyClass
  force push ApexClass MyClass
  force push src/classes/MyClass.cls
`,
}

var xmlBody = `<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">%s
    <version>29.0</version>
</Package>`

var xmlType = `
    <types>%s
        <name>%s</name>
    </types>`
var xmlMember = `
        <members>%s</members>`

type metapath struct {
    path string
    name string
}

var metapaths = []metapath{
    metapath{"classes"      , "ApexClass"},
    metapath{"objects"      , "CustomObject"},
    metapath{"tabs"         , "CustomTab"},
    metapath{"flexipages"   , "FlexiPage"},
    metapath{"components"   , "ApexComponent"},
    metapath{"triggers"     , "ApexTrigger"},
    metapath{"pages"        , "ApexPage"},
}

func getPathForMeta(metaname string) string {
    for _, mp := range metapaths {
        if strings.ToLower(mp.name) == strings.ToLower(metaname) {
            return mp.path
        }
    }

    // Unknown, so use metaname
    return metaname
}

func getMetaForPath(path string) string {
    for _, mp := range metapaths {
        if mp.path == path {
            return mp.name
        }
    }

    // Unknown, so use path
    return path
}

func argIsFile(fpath string) bool {
    if _, err := os.Stat(fpath); err != nil {
        return false
    }
    return true
}

func runPush(cmd *Command, args []string) {
	if len(args) == 0 {
		cmd.printUsage()
		return
	}

    if argIsFile(args[0]) {
        fpath := args[0]
        pushByPaths(args)

        fmt.Printf("Pushed %s to Force.com\n", fpath)
        return
    }

	if len(args) == 2 {
        // If arg[0] is already path or meta, the method will return arg[0]
        objPath := getPathForMeta(args[0])
        objName := args[1]
        pushByName(objPath, objName)

        fmt.Printf("Pushed %s to Force.com\n", objName)
        return
    }

    fmt.Println("Could not find file or determine metadata")

    // If we got here, something is not valid
    cmd.printUsage()
}

func pushByName(objPath string, objName string) {
	wd, _ := os.Getwd()

    // First try for metadata directory
	root := filepath.Join(wd, "metadata")
	if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
        // If not found, try for src directory
        root = filepath.Join(wd, "src")
        if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
            ErrorAndExit("Current directory must contain a src or metadata directory")
        }
	}

	if _, err := os.Stat(filepath.Join(root, objPath)); os.IsNotExist(err) {
		ErrorAndExit("Folder " + objPath + " not found, must specify valid metadata")
	}

    // Find file by walking directory and ignoring extension
	found := false
    fpath := ""
	err := filepath.Walk(filepath.Join(root, objPath), func(path string, f os.FileInfo, err error) error {
		if f.Mode().IsRegular() {
            fname := strings.ToLower(f.Name())
            fname = strings.TrimSuffix(fname, filepath.Ext(fname))
			if strings.ToLower(fname) == strings.ToLower(objName) {
				found = true
                fpath = filepath.Join(root, objPath, f.Name())
			}
		}
		return nil
	})
	if err != nil {
		ErrorAndExit(err.Error())
	}
	if !found {
		ErrorAndExit("Could not find " + objName + " in " + objPath)
	}

    pushByPath(fpath)
}

func pushByPath(fpath string) {
    pushByPaths([]string{fpath})
}

// Push metadata object by path to a file
func pushByPaths(fpaths []string) {
	files := make(ForceMetadataFiles)
    xmlMap := make(map[string][]string)

    for _, fpath := range fpaths{
        addFile(files, xmlMap, fpath)
    }

    files["package.xml"] = buildXml(xmlMap)

    deployFiles(files)
}

func addFile(files ForceMetadataFiles, xmlMap map[string][]string, fpath string) {
    fpath, err := filepath.Abs(fpath)
    if err != nil {
        ErrorAndExit("Cound not find " + fpath)
    }
    if _, err := os.Stat(fpath); err != nil {
        ErrorAndExit("Cound not open " + fpath)
    }

    hasMeta := true
    fname := filepath.Base(fpath)
    fname = strings.TrimSuffix(fname, filepath.Ext(fname))
    fdir := filepath.Dir(fpath)
    typePath := filepath.Base(fdir)
    srcDir := filepath.Dir(fdir)
    metaType := getMetaForPath(typePath)
    // Should be present since we worked back to srcDir
    frel, _ := filepath.Rel(srcDir, fpath)

    // Try to find meta file
    fmeta := fpath + "-meta.xml"
    fmetarel := ""
    if _, err := os.Stat(fmeta); err != nil {
        if os.IsNotExist(err) {
            hasMeta = false
        } else {
            ErrorAndExit("Cound not open " + fmeta)
        }
    } else {
        // Should be present since we worked back to srcDir
        fmetarel, _ = filepath.Rel(srcDir, fmeta)
    }

    xmlMap[metaType] = append(xmlMap[metaType], fname)

    fdata, err := ioutil.ReadFile(fpath)
    files[frel] = fdata
    if hasMeta {
        fdata, err = ioutil.ReadFile(fmeta)
        files[fmetarel] = fdata
    }
}

func buildXml(xmlMap map[string][]string) []byte {
    var typeXml string
    for metaType, members := range xmlMap{
        fmt.Println("Type: " + metaType)
        var membersXml string
        for _, member := range members{
            fmt.Println("member: " + member)
            membersXml += fmt.Sprintf(xmlMember, member)
        }

        if membersXml != "" {
            typeXml += fmt.Sprintf(xmlType, membersXml, metaType)
        }
    }

    bodyXml := fmt.Sprintf(xmlBody, typeXml)

    return []byte(bodyXml)
}

func deployFiles(files ForceMetadataFiles) {
	force, _ := ActiveForce()
	var DeploymentOptions ForceDeployOptions
	successes, problems, err := force.Metadata.Deploy(files, DeploymentOptions)
	if err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Printf("\nFailures - %d\n", len(problems))
	for _, problem := range problems {
		if problem.FullName == "" {
			fmt.Println(problem.Problem)
		} else {
			fmt.Printf("ERROR with %s:\n %s\n", problem.FullName, problem.Problem)
		}
	}

	fmt.Printf("\nSuccesses - %d\n", len(successes))
	for _, success := range successes {
		if success.FullName != "package.xml" {
			verb := "unchanged"
			if success.Changed {
				verb = "changed"
			} else if success.Deleted {
				verb = "deleted"
			} else if success.Created {
				verb = "created"
			}
			fmt.Printf("%s\n\tstatus: %s\n\tid=%s\n", success.FullName, verb, success.Id)
		}
	}
}
<commit_msg>Format push.go<commit_after>package main

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

var cmdPush = &Command{
	Run:   runPush,
	Usage: "push (<metadata> <name> | <file>...)",
	Short: "Deploy artifact from a local directory",
	Long: `
Deploy artifact from a local directory
<metadata>: Accepts either actual directory name or Metadata type

Examples:
  force push classes MyClass
  force push ApexClass MyClass
  force push src/classes/MyClass.cls
`,
}

var xmlBody = `<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">%s
    <version>29.0</version>
</Package>`

var xmlType = `
    <types>%s
        <name>%s</name>
    </types>`
var xmlMember = `
        <members>%s</members>`

type metapath struct {
	path string
	name string
}

var metapaths = []metapath{
	metapath{"classes", "ApexClass"},
	metapath{"objects", "CustomObject"},
	metapath{"tabs", "CustomTab"},
	metapath{"flexipages", "FlexiPage"},
	metapath{"components", "ApexComponent"},
	metapath{"triggers", "ApexTrigger"},
	metapath{"pages", "ApexPage"},
}

func getPathForMeta(metaname string) string {
	for _, mp := range metapaths {
		if strings.ToLower(mp.name) == strings.ToLower(metaname) {
			return mp.path
		}
	}

	// Unknown, so use metaname
	return metaname
}

func getMetaForPath(path string) string {
	for _, mp := range metapaths {
		if mp.path == path {
			return mp.name
		}
	}

	// Unknown, so use path
	return path
}

func argIsFile(fpath string) bool {
	if _, err := os.Stat(fpath); err != nil {
		return false
	}
	return true
}

func runPush(cmd *Command, args []string) {
	if len(args) == 0 {
		cmd.printUsage()
		return
	}

	if argIsFile(args[0]) {
		fpath := args[0]
		pushByPaths(args)

		fmt.Printf("Pushed %s to Force.com\n", fpath)
		return
	}

	if len(args) == 2 {
		// If arg[0] is already path or meta, the method will return arg[0]
		objPath := getPathForMeta(args[0])
		objName := args[1]
		pushByName(objPath, objName)

		fmt.Printf("Pushed %s to Force.com\n", objName)
		return
	}

	fmt.Println("Could not find file or determine metadata")

	// If we got here, something is not valid
	cmd.printUsage()
}

func pushByName(objPath string, objName string) {
	wd, _ := os.Getwd()

	// First try for metadata directory
	root := filepath.Join(wd, "metadata")
	if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
		// If not found, try for src directory
		root = filepath.Join(wd, "src")
		if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
			ErrorAndExit("Current directory must contain a src or metadata directory")
		}
	}

	if _, err := os.Stat(filepath.Join(root, objPath)); os.IsNotExist(err) {
		ErrorAndExit("Folder " + objPath + " not found, must specify valid metadata")
	}

	// Find file by walking directory and ignoring extension
	found := false
	fpath := ""
	err := filepath.Walk(filepath.Join(root, objPath), func(path string, f os.FileInfo, err error) error {
		if f.Mode().IsRegular() {
			fname := strings.ToLower(f.Name())
			fname = strings.TrimSuffix(fname, filepath.Ext(fname))
			if strings.ToLower(fname) == strings.ToLower(objName) {
				found = true
				fpath = filepath.Join(root, objPath, f.Name())
			}
		}
		return nil
	})
	if err != nil {
		ErrorAndExit(err.Error())
	}
	if !found {
		ErrorAndExit("Could not find " + objName + " in " + objPath)
	}

	pushByPath(fpath)
}

func pushByPath(fpath string) {
	pushByPaths([]string{fpath})
}

// Push metadata object by path to a file
func pushByPaths(fpaths []string) {
	files := make(ForceMetadataFiles)
	xmlMap := make(map[string][]string)

	for _, fpath := range fpaths {
		addFile(files, xmlMap, fpath)
	}

	files["package.xml"] = buildXml(xmlMap)

	deployFiles(files)
}

func addFile(files ForceMetadataFiles, xmlMap map[string][]string, fpath string) {
	fpath, err := filepath.Abs(fpath)
	if err != nil {
		ErrorAndExit("Cound not find " + fpath)
	}
	if _, err := os.Stat(fpath); err != nil {
		ErrorAndExit("Cound not open " + fpath)
	}

	hasMeta := true
	fname := filepath.Base(fpath)
	fname = strings.TrimSuffix(fname, filepath.Ext(fname))
	fdir := filepath.Dir(fpath)
	typePath := filepath.Base(fdir)
	srcDir := filepath.Dir(fdir)
	metaType := getMetaForPath(typePath)
	// Should be present since we worked back to srcDir
	frel, _ := filepath.Rel(srcDir, fpath)

	// Try to find meta file
	fmeta := fpath + "-meta.xml"
	fmetarel := ""
	if _, err := os.Stat(fmeta); err != nil {
		if os.IsNotExist(err) {
			hasMeta = false
		} else {
			ErrorAndExit("Cound not open " + fmeta)
		}
	} else {
		// Should be present since we worked back to srcDir
		fmetarel, _ = filepath.Rel(srcDir, fmeta)
	}

	xmlMap[metaType] = append(xmlMap[metaType], fname)

	fdata, err := ioutil.ReadFile(fpath)
	files[frel] = fdata
	if hasMeta {
		fdata, err = ioutil.ReadFile(fmeta)
		files[fmetarel] = fdata
	}
}

func buildXml(xmlMap map[string][]string) []byte {
	var typeXml string
	for metaType, members := range xmlMap {
		fmt.Println("Type: " + metaType)
		var membersXml string
		for _, member := range members {
			fmt.Println("member: " + member)
			membersXml += fmt.Sprintf(xmlMember, member)
		}

		if membersXml != "" {
			typeXml += fmt.Sprintf(xmlType, membersXml, metaType)
		}
	}

	bodyXml := fmt.Sprintf(xmlBody, typeXml)

	return []byte(bodyXml)
}

func deployFiles(files ForceMetadataFiles) {
	force, _ := ActiveForce()
	var DeploymentOptions ForceDeployOptions
	successes, problems, err := force.Metadata.Deploy(files, DeploymentOptions)
	if err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Printf("\nFailures - %d\n", len(problems))
	for _, problem := range problems {
		if problem.FullName == "" {
			fmt.Println(problem.Problem)
		} else {
			fmt.Printf("ERROR with %s:\n %s\n", problem.FullName, problem.Problem)
		}
	}

	fmt.Printf("\nSuccesses - %d\n", len(successes))
	for _, success := range successes {
		if success.FullName != "package.xml" {
			verb := "unchanged"
			if success.Changed {
				verb = "changed"
			} else if success.Deleted {
				verb = "deleted"
			} else if success.Created {
				verb = "created"
			}
			fmt.Printf("%s\n\tstatus: %s\n\tid=%s\n", success.FullName, verb, success.Id)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/asn1"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/mdigger/log"
	"golang.org/x/crypto/pkcs12"
	"golang.org/x/net/http2"
)

// Push описывает конфигурация для отправки уведомлений через сервисы
// Apple Push Notification и Firebase Cloud Messaging.
type Push struct {
	apns  map[string]*http.Transport // сертификаты для Apple Push
	fcm   map[string]string          // ключи для Firebase Cloud Messages
	store *Store                     // хранилище токенов
}

// Send отсылает уведомление на все устройства пользователя.
func (p *Push) Send(login string, obj interface{}) {
	// запускаем параллельно отсылку пушей
	go func() {
		if err := p.sendAPN(login, obj); err != nil {
			log.WithError(err).Error("send Apple Notification error")
		}
	}()
	go func() {
		if err := p.sendFCM(login, obj); err != nil {
			log.WithError(err).Error("send Firebase Cloud Messages error")
		}
	}()
}

// sendAPN отсылает уведомление на все Apple устройства пользователя.
func (p *Push) sendAPN(login string, obj interface{}) error {
	// преобразуем данные для пуша в формат JSON
	var payload []byte
	switch obj := obj.(type) {
	case []byte:
		payload = obj
	case string:
		payload = []byte(obj)
	case json.RawMessage:
		payload = []byte(obj)
	default:
		var err error
		payload, err = json.Marshal(obj)
		if err != nil {
			log.WithError(err).Error("push payload to json error")
			return err
		}
	}
	var client = &http.Client{
		Timeout: time.Second * 5,
	}
	for topic, transport := range p.apns {
		// получаем список токенов пользователя для данного сертификата
		var tokens = p.store.ListTokens("apn", topic, login)
		if len(tokens) == 0 {
			continue
		}
		client.Transport = transport
		// задаем хост в зависимости от sandbox
		var host string
		if topic[len(topic)-1] != '~' {
			host = "https://api.push.apple.com"
		} else {
			host = "https://api.development.push.apple.com"
		}
		// для каждого токена устройства формируем отдельный запрос
		var success, failure int // счетчики
		for _, token := range tokens {
			req, err := http.NewRequest("POST", host+"/3/device/"+token,
				bytes.NewReader(payload))
			if err != nil {
				return err
			}
			req.Header.Set("user-agent", agent)
			req.Header.Set("Content-Type", "application/json")
			resp, err := client.Do(req)
			if err != nil {
				break
			}
			if resp.StatusCode == http.StatusOK {
				resp.Body.Close()
				success++
				continue
			}
			failure++
			// разбираем ответ сервера с описанием ошибки
			var apnsError = new(struct {
				Reason string `json:"reason"`
			})
			err = json.NewDecoder(resp.Body).Decode(apnsError)
			resp.Body.Close()
			if err != nil {
				continue
			}
			// в случае ошибки связанной с токеном устройства, удаляем его
			switch apnsError.Reason {
			case "MissingDeviceToken",
				"BadDeviceToken",
				"DeviceTokenNotForTopic",
				"Unregistered":
				p.store.RemoveToken("apn", topic, token)
			default:
			}
			log.WithFields(log.Fields{
				"topic":  topic,
				"token":  token,
				"reason": apnsError.Reason,
			}).Debug("apple push error")
		}
		log.WithFields(log.Fields{
			"topic":   topic,
			"success": success,
			"failure": failure,
		}).Info("apple push")
	}
	return nil
}

// sendFCM отсылает уведомление на все Google устройства пользователя.
func (p *Push) sendFCM(login string, obj interface{}) error {
	var client = &http.Client{Timeout: time.Second * 5}
	for appName, fcmKey := range p.fcm {
		// получаем список токенов пользователя для данного сертификата
		var tokens = p.store.ListTokens("fcm", appName, login)
		if len(tokens) == 0 {
			continue
		}
		// формируем данные для отправки (без визуальной составляющей пуша:
		// только данные)
		var gfcmMsg = &struct {
			RegistrationIDs []string    `json:"registration_ids,omitempty"`
			Data            interface{} `json:"data,omitempty"`
			TTL             uint16      `json:"time_to_live"`
		}{
			// т.к. тут только устройства ОДНОГО пользователя, то
			// ограничением на количество токенов можно пренебречь
			RegistrationIDs: tokens,
			Data:            obj, // добавляем уже сформированные ранее данные
			// время жизни сообщения TTL = 0, поэтому оно не кешируется
			// на сервере, а сразу отправляется пользователю: для пушей
			// оо звонках мне показалось это наиболее актуальным.
		}
		// приводим к формату JSON
		data, err := json.Marshal(gfcmMsg)
		if err != nil {
			return err
		}
		req, err := http.NewRequest("POST",
			"https://fcm.googleapis.com/fcm/send", bytes.NewReader(data))
		if err != nil {
			return err
		}
		req.Header.Set("User-Agent", agent)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "key="+fcmKey)
		resp, err := client.Do(req)
		if err != nil {
			return err
		}
		// проверяем статус ответа
		if resp.StatusCode != http.StatusOK {
			resp.Body.Close()
			return err
		}
		// разбираем ответ сервера
		var result = new(struct {
			Success int `json:"success"`
			Failure int `json:"failure"`
			Results []struct {
				RegistrationID string `json:"registration_id"`
				Error          string `json:"error"`
			} `json:"results"`
		})
		err = json.NewDecoder(resp.Body).Decode(result)
		resp.Body.Close()
		if err != nil {
			return err
		}
		// проходим по массиву результатов в ответе для каждого токена
		for indx, result := range result.Results {
			switch result.Error {
			case "":
				// нет ошибки - доставлено
				// проверяем, что, возможно, токен устарел и его нужно
				// заменить на более новый, который указан в ответе
				if result.RegistrationID != "" {
					token := gfcmMsg.RegistrationIDs[indx]
					p.store.RemoveToken("fcm", appName, token)
					p.store.AddToken("fcm", appName, result.RegistrationID, login)
				}
			case "Unavailable":
				// устройство в данный момент не доступно
			default:
				// все остальное представляет из себя, так или иначе,
				// ошибки, связанные с неверным токеном устройства
				token := gfcmMsg.RegistrationIDs[indx]
				p.store.RemoveToken("fcm", appName, token)
			}
		}
		log.WithFields(log.Fields{
			"app":     appName,
			"success": result.Success,
			"failure": result.Failure,
		}).Info("google push")
	}
	return nil
}

// Support возвращает true, если данная тема поддерживается в качестве
// уведомления.
func (p *Push) Support(kind, topic string) bool {
	switch kind {
	case "apn":
		_, ok := p.apns[topic]
		return ok
	case "fcm":
		_, ok := p.fcm[topic]
		return ok
	default:
		return false
	}
}

// LoadCertificate загружает сертификат для Apple Push и сохраняем во внутреннем
// списке подготовленный для него http.Transport.
func (p *Push) LoadCertificate(filename, password string) error {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	privateKey, x509Cert, err := pkcs12.Decode(data, password)
	if err != nil {
		return err
	}
	if _, err = x509Cert.Verify(x509.VerifyOptions{}); err != nil {
		if _, ok := err.(x509.UnknownAuthorityError); !ok {
			return err
		}
	}
	var topicID string
	for _, attr := range x509Cert.Subject.Names {
		if attr.Type.Equal(typeBundle) {
			topicID = attr.Value.(string)
			break
		}
	}
	var transport = &http.Transport{
		TLSClientConfig: &tls.Config{
			Certificates: []tls.Certificate{
				tls.Certificate{
					Certificate: [][]byte{x509Cert.Raw},
					PrivateKey:  privateKey,
					Leaf:        nil,
				},
			},
		},
	}
	if err = http2.ConfigureTransport(transport); err != nil {
		return err
	}
	if p.apns == nil {
		p.apns = make(map[string]*http.Transport)
	}
	for _, attr := range x509Cert.Extensions {
		switch t := attr.Id; {
		case t.Equal(typeDevelopmet): // Development
			p.apns[topicID+"~"] = transport
		case t.Equal(typeProduction): // Production
			p.apns[topicID] = transport
		case t.Equal(typeTopics): // Topics
			// не поддерживаем сертификаты с несколькими темами, т.к. для них
			// нужна более сложная работа
			return errors.New("apns certificate with topics not supported")
		}
	}
	log.WithFields(log.Fields{
		"file":   filename,
		"topic":  topicID,
		"expire": x509Cert.NotAfter.Format("2006-01-02"),
	}).Info("apple push certificate")
	return nil
}

var (
	typeBundle     = asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 1}
	typeDevelopmet = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 3, 1}
	typeProduction = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 3, 2}
	typeTopics     = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 3, 6}
)
<commit_msg>apn push error log<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/asn1"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/mdigger/log"
	"golang.org/x/crypto/pkcs12"
	"golang.org/x/net/http2"
)

// Push описывает конфигурация для отправки уведомлений через сервисы
// Apple Push Notification и Firebase Cloud Messaging.
type Push struct {
	apns  map[string]*http.Transport // сертификаты для Apple Push
	fcm   map[string]string          // ключи для Firebase Cloud Messages
	store *Store                     // хранилище токенов
}

// Send отсылает уведомление на все устройства пользователя.
func (p *Push) Send(login string, obj interface{}) {
	// запускаем параллельно отсылку пушей
	go func() {
		if err := p.sendAPN(login, obj); err != nil {
			log.WithError(err).Error("send Apple Notification error")
		}
	}()
	go func() {
		if err := p.sendFCM(login, obj); err != nil {
			log.WithError(err).Error("send Firebase Cloud Messages error")
		}
	}()
}

// sendAPN отсылает уведомление на все Apple устройства пользователя.
func (p *Push) sendAPN(login string, obj interface{}) error {
	// преобразуем данные для пуша в формат JSON
	var payload []byte
	switch obj := obj.(type) {
	case []byte:
		payload = obj
	case string:
		payload = []byte(obj)
	case json.RawMessage:
		payload = []byte(obj)
	default:
		var err error
		payload, err = json.Marshal(obj)
		if err != nil {
			log.WithError(err).Error("push payload to json error")
			return err
		}
	}
	var client = &http.Client{
		Timeout: time.Second * 5,
	}
	for topic, transport := range p.apns {
		// получаем список токенов пользователя для данного сертификата
		var tokens = p.store.ListTokens("apn", topic, login)
		if len(tokens) == 0 {
			continue
		}
		client.Transport = transport
		// задаем хост в зависимости от sandbox
		var host string
		if topic[len(topic)-1] != '~' {
			host = "https://api.push.apple.com"
		} else {
			host = "https://api.development.push.apple.com"
		}
		// для каждого токена устройства формируем отдельный запрос
		var success, failure int // счетчики
		for _, token := range tokens {
			req, err := http.NewRequest("POST", host+"/3/device/"+token,
				bytes.NewReader(payload))
			if err != nil {
				return err
			}
			req.Header.Set("user-agent", agent)
			req.Header.Set("Content-Type", "application/json")
			resp, err := client.Do(req)
			if err != nil {
				log.WithError(err).Error("apn send error")
				break
			}
			if resp.StatusCode == http.StatusOK {
				resp.Body.Close()
				success++
				continue
			}
			failure++
			// разбираем ответ сервера с описанием ошибки
			var apnsError = new(struct {
				Reason string `json:"reason"`
			})
			err = json.NewDecoder(resp.Body).Decode(apnsError)
			resp.Body.Close()
			if err != nil {
				continue
			}
			// в случае ошибки связанной с токеном устройства, удаляем его
			switch apnsError.Reason {
			case "MissingDeviceToken",
				"BadDeviceToken",
				"DeviceTokenNotForTopic",
				"Unregistered":
				p.store.RemoveToken("apn", topic, token)
			default:
			}
			log.WithFields(log.Fields{
				"topic":  topic,
				"token":  token,
				"reason": apnsError.Reason,
			}).Debug("apple push error")
		}
		log.WithFields(log.Fields{
			"topic":   topic,
			"success": success,
			"failure": failure,
		}).Info("apple push")
	}
	return nil
}

// sendFCM отсылает уведомление на все Google устройства пользователя.
func (p *Push) sendFCM(login string, obj interface{}) error {
	var client = &http.Client{Timeout: time.Second * 5}
	for appName, fcmKey := range p.fcm {
		// получаем список токенов пользователя для данного сертификата
		var tokens = p.store.ListTokens("fcm", appName, login)
		if len(tokens) == 0 {
			continue
		}
		// формируем данные для отправки (без визуальной составляющей пуша:
		// только данные)
		var gfcmMsg = &struct {
			RegistrationIDs []string    `json:"registration_ids,omitempty"`
			Data            interface{} `json:"data,omitempty"`
			TTL             uint16      `json:"time_to_live"`
		}{
			// т.к. тут только устройства ОДНОГО пользователя, то
			// ограничением на количество токенов можно пренебречь
			RegistrationIDs: tokens,
			Data:            obj, // добавляем уже сформированные ранее данные
			// время жизни сообщения TTL = 0, поэтому оно не кешируется
			// на сервере, а сразу отправляется пользователю: для пушей
			// оо звонках мне показалось это наиболее актуальным.
		}
		// приводим к формату JSON
		data, err := json.Marshal(gfcmMsg)
		if err != nil {
			return err
		}
		req, err := http.NewRequest("POST",
			"https://fcm.googleapis.com/fcm/send", bytes.NewReader(data))
		if err != nil {
			return err
		}
		req.Header.Set("User-Agent", agent)
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Authorization", "key="+fcmKey)
		resp, err := client.Do(req)
		if err != nil {
			return err
		}
		// проверяем статус ответа
		if resp.StatusCode != http.StatusOK {
			resp.Body.Close()
			return err
		}
		// разбираем ответ сервера
		var result = new(struct {
			Success int `json:"success"`
			Failure int `json:"failure"`
			Results []struct {
				RegistrationID string `json:"registration_id"`
				Error          string `json:"error"`
			} `json:"results"`
		})
		err = json.NewDecoder(resp.Body).Decode(result)
		resp.Body.Close()
		if err != nil {
			return err
		}
		// проходим по массиву результатов в ответе для каждого токена
		for indx, result := range result.Results {
			switch result.Error {
			case "":
				// нет ошибки - доставлено
				// проверяем, что, возможно, токен устарел и его нужно
				// заменить на более новый, который указан в ответе
				if result.RegistrationID != "" {
					token := gfcmMsg.RegistrationIDs[indx]
					p.store.RemoveToken("fcm", appName, token)
					p.store.AddToken("fcm", appName, result.RegistrationID, login)
				}
			case "Unavailable":
				// устройство в данный момент не доступно
			default:
				// все остальное представляет из себя, так или иначе,
				// ошибки, связанные с неверным токеном устройства
				token := gfcmMsg.RegistrationIDs[indx]
				p.store.RemoveToken("fcm", appName, token)
			}
		}
		log.WithFields(log.Fields{
			"app":     appName,
			"success": result.Success,
			"failure": result.Failure,
		}).Info("google push")
	}
	return nil
}

// Support возвращает true, если данная тема поддерживается в качестве
// уведомления.
func (p *Push) Support(kind, topic string) bool {
	switch kind {
	case "apn":
		_, ok := p.apns[topic]
		return ok
	case "fcm":
		_, ok := p.fcm[topic]
		return ok
	default:
		return false
	}
}

// LoadCertificate загружает сертификат для Apple Push и сохраняем во внутреннем
// списке подготовленный для него http.Transport.
func (p *Push) LoadCertificate(filename, password string) error {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	privateKey, x509Cert, err := pkcs12.Decode(data, password)
	if err != nil {
		return err
	}
	if _, err = x509Cert.Verify(x509.VerifyOptions{}); err != nil {
		if _, ok := err.(x509.UnknownAuthorityError); !ok {
			return err
		}
	}
	var topicID string
	for _, attr := range x509Cert.Subject.Names {
		if attr.Type.Equal(typeBundle) {
			topicID = attr.Value.(string)
			break
		}
	}
	var transport = &http.Transport{
		TLSClientConfig: &tls.Config{
			Certificates: []tls.Certificate{
				tls.Certificate{
					Certificate: [][]byte{x509Cert.Raw},
					PrivateKey:  privateKey,
					Leaf:        nil,
				},
			},
		},
	}
	if err = http2.ConfigureTransport(transport); err != nil {
		return err
	}
	if p.apns == nil {
		p.apns = make(map[string]*http.Transport)
	}
	for _, attr := range x509Cert.Extensions {
		switch t := attr.Id; {
		case t.Equal(typeDevelopmet): // Development
			p.apns[topicID+"~"] = transport
		case t.Equal(typeProduction): // Production
			p.apns[topicID] = transport
		case t.Equal(typeTopics): // Topics
			// не поддерживаем сертификаты с несколькими темами, т.к. для них
			// нужна более сложная работа
			return errors.New("apns certificate with topics not supported")
		}
	}
	log.WithFields(log.Fields{
		"file":   filename,
		"topic":  topicID,
		"expire": x509Cert.NotAfter.Format("2006-01-02"),
	}).Info("apple push certificate")
	return nil
}

var (
	typeBundle     = asn1.ObjectIdentifier{0, 9, 2342, 19200300, 100, 1, 1}
	typeDevelopmet = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 3, 1}
	typeProduction = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 3, 2}
	typeTopics     = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 6, 3, 6}
)
<|endoftext|>


<commit_before>package parser

import (
	"errors"
	"tokenizer"
	"types"
)

const new_line = "\r\n"

// Parses tokens, validates code to a specific degree
// and writes SQF code into desired location.
func (c *Compiler) Parse(token []tokenizer.Token, prettyPrinting bool) string {
	if !c.initParser(token, prettyPrinting) {
		return ""
	}

	for c.tokenIndex < len(token) {
		c.parseBlock()
	}

	return c.out
}

func (c *Compiler) parseBlock() {
	if c.get().Preprocessor {
		c.parsePreprocessor()
	} else if c.accept("var") {
		c.parseVar()
	} else if c.accept("if") {
		c.parseIf()
	} else if c.accept("while") {
		c.parseWhile()
	} else if c.accept("switch") {
		c.parseSwitch()
	} else if c.accept("for") {
		c.parseFor()
	} else if c.accept("foreach") {
		c.parseForeach()
	} else if c.accept("func") {
		c.parseFunction()
	} else if c.accept("return") {
		c.parseReturn()
	} else if c.accept("try") {
		c.parseTryCatch()
	} else if c.accept("exitwith") {
		c.parseExitWith()
	} else if c.accept("waituntil") {
		c.parseWaitUntil()
	} else if c.accept("case") || c.accept("default") {
		return
	} else {
		c.parseStatement()
	}

	if !c.end() && !c.accept("}") {
		c.parseBlock()
	}
}

func (c *Compiler) parsePreprocessor() {
	// we definitely want a new line before and after
	c.appendOut(new_line+c.get().Token+new_line, false)
	c.next()
}

func (c *Compiler) parseVar() {
	c.expect("var")
	c.appendOut(c.get().Token, false)
	c.next()

	if c.accept("=") {
		c.next()
		c.appendOut(" = ", false)
		c.parseExpression(true)
	}

	c.expect(";")
	c.appendOut(";", true)
}

func (c *Compiler) parseArray(out bool) string {
	output := ""
	c.expect("[")
	output += "["

	if !c.accept("]") {
		output += c.parseExpression(false)

		for c.accept(",") {
			c.next()
			output += "," + c.parseExpression(false)
		}
	}

	c.expect("]")
	output += "]"

	if out {
		c.appendOut(output, false)
	}

	return output
}

func (c *Compiler) parseIf() {
	c.expect("if")
	c.appendOut("if (", false)
	c.parseExpression(true)
	c.appendOut(") then {", true)
	c.expect("{")
	c.parseBlock()
	c.expect("}")

	if c.accept("else") {
		c.next()
		c.expect("{")
		c.appendOut("} else {", true)
		c.parseBlock()
		c.expect("}")
	}

	c.appendOut("};", true)
}

func (c *Compiler) parseWhile() {
	c.expect("while")
	c.appendOut("while {", false)
	c.parseExpression(true)
	c.appendOut("} do {", true)
	c.expect("{")
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseSwitch() {
	c.expect("switch")
	c.appendOut("switch (", false)
	c.parseExpression(true)
	c.appendOut(") do {", true)
	c.expect("{")
	c.parseSwitchBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseSwitchBlock() {
	if c.accept("}") {
		return
	}

	if c.accept("case") {
		c.next()
		c.appendOut("case ", false)
		c.parseExpression(true)
		c.expect(":")
		c.appendOut(":", true)

		if !c.accept("case") && !c.accept("}") && !c.accept("default") {
			c.appendOut("{", true)
			c.parseBlock()
			c.appendOut("};", true)
		}
	} else if c.accept("default") {
		c.next()
		c.expect(":")
		c.appendOut("default:", true)

		if !c.accept("}") {
			c.appendOut("{", true)
			c.parseBlock()
			c.appendOut("};", true)
		}
	}

	c.parseSwitchBlock()
}

func (c *Compiler) parseFor() {
	c.expect("for")
	c.appendOut("for [{", false)

	// var in first assignment is optional
	if c.accept("var") {
		c.next()
	}

	c.parseExpression(true)
	c.expect(";")
	c.appendOut("}, {", false)
	c.parseExpression(true)
	c.expect(";")
	c.appendOut("}, {", false)
	c.parseExpression(true)
	c.appendOut("}] do {", true)
	c.expect("{")
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseForeach() {
	c.expect("foreach")
	element := c.get().Token
	c.next()
	c.expect("=")
	c.expect(">")
	expr := c.parseExpression(false)
	c.expect("{")
	c.appendOut("{", true)
	c.appendOut(element+" = _x;", true)
	c.parseBlock()
	c.expect("}")
	c.appendOut("} forEach ("+expr+");", true)
}

func (c *Compiler) parseFunction() {
	c.expect("func")

	// check for build in function
	if buildin := types.GetFunction(c.get().Token); buildin != nil {
		panic(errors.New(c.get().Token + " is a build in function, choose a different name"))
	}

	c.appendOut(c.get().Token+" = {", true)
	c.next()
	c.expect("(")
	c.parseFunctionParameter()
	c.expect(")")
	c.expect("{")
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseFunctionParameter() {
	// empty parameter list
	if c.accept("{") {
		return
	}

	c.appendOut("params [", false)

	for !c.accept(")") {
		name := c.get().Token
		c.next()

		if c.accept("=") {
			c.next()
			value := c.get().Token
			c.next()
			c.appendOut("[\""+name+"\","+value+"]", false)
		} else {
			c.appendOut("\""+name+"\"", false)
		}

		if !c.accept(")") {
			c.expect(",")
			c.appendOut(",", false)
		}
	}

	c.appendOut("];", true)
}

func (c *Compiler) parseReturn() {
	c.expect("return")
	c.appendOut("return ", false)
	c.parseExpression(true)
	c.expect(";")
	c.appendOut(";", true)
}

func (c *Compiler) parseTryCatch() {
	c.expect("try")
	c.expect("{")
	c.appendOut("try {", true)
	c.parseBlock()
	c.expect("}")
	c.expect("catch")
	c.expect("{")
	c.appendOut("} catch {", true)
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseExitWith() {
	c.expect("exitwith")
	c.expect("{")
	c.appendOut("if (true) exitWith {", true)
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseWaitUntil() {
	c.expect("waituntil")
	c.expect("(")
	c.appendOut("waitUntil {", false)
	c.parseExpression(true)

	if c.accept(";") {
		c.next()
		c.appendOut(";", false)
		c.parseExpression(true)
	}

	c.expect(")")
	c.expect(";")
	c.appendOut("};", true)
}

func (c *Compiler) parseInlineCode() string {
	c.expect("code")
	c.expect("(")

	code := c.get().Token
	c.next()
	output := "{}"

	if len(code) > 2 {
		compiler := Compiler{}
		output = "{" + compiler.Parse(tokenizer.Tokenize([]byte(code[1:len(code)-1]), true), false) + "}"
	}

	c.expect(")")

	return output
}

// Everything that does not start with a keyword.
func (c *Compiler) parseStatement() {
	// empty block
	if c.accept("}") || c.accept("case") || c.accept("default") {
		return
	}

	// variable or function name
	name := c.get().Token
	c.next()

	if c.accept("=") {
		c.appendOut(name, false)
		c.parseAssignment()
	} else {
		c.parseFunctionCall(true, name)
		c.expect(";")
		c.appendOut(";", true)
	}

	if !c.end() {
		c.parseBlock()
	}
}

func (c *Compiler) parseAssignment() {
	c.expect("=")
	c.appendOut(" = ", false)
	c.parseExpression(true)
	c.expect(";")
	c.appendOut(";", true)
}

func (c *Compiler) parseFunctionCall(out bool, name string) string {
	output := ""

	c.expect("(")
	paramsStr, paramCount := c.parseParameter(false)
	c.expect(")")

	// buildin function
	buildin := types.GetFunction(name)

	if buildin != nil {
		if buildin.Type == types.NULL {
			output = name
		} else if buildin.Type == types.UNARY {
			output = c.parseUnaryFunction(name, paramsStr, paramCount)
		} else {
			output = c.parseBinaryFunction(name, paramsStr, buildin, paramCount)
		}
	} else {
		output = "[" + paramsStr + "] call " + name
	}

	if out {
		c.appendOut(output, false)
	}

	return output
}

func (c *Compiler) parseUnaryFunction(name, paramsStr string, paramCount int) string {
	output := ""

	if paramCount == 1 {
		output = name + " " + paramsStr
	} else {
		output = "[" + paramsStr + "] call " + name
	}

	return output
}

func (c *Compiler) parseBinaryFunction(name string, leftParamsStr string, buildin *types.FunctionType, paramCount int) string {
	output := ""

	c.next()
	rightParamsStr, rightParamCount := c.parseParameter(false)
	c.expect(")")

	if paramCount > 1 {
		leftParamsStr = "[" + leftParamsStr + "]"
	}

	if rightParamCount > 1 {
		rightParamsStr = "[" + rightParamsStr + "]"
	}

	if paramCount > 0 {
		output = leftParamsStr + " " + name + " " + rightParamsStr
	} else {
		output = name + " " + rightParamsStr
	}

	return output
}

func (c *Compiler) parseParameter(out bool) (string, int) {
	output := ""
	count := 0

	for !c.accept(")") {
		expr := c.parseExpression(out)
		output += expr
		count++

		if !c.accept(")") {
			c.expect(",")
			output += ", "
		}
	}

	if out {
		c.appendOut(output, false)
	}

	return output, count
}

func (c *Compiler) parseExpression(out bool) string {
	output := c.parseArith()

	for c.accept("<") || c.accept(">") || c.accept("&") || c.accept("|") || c.accept("=") || c.accept("!") {
		if c.accept("<") {
			output += "<"
			c.next()
		} else if c.accept(">") {
			output += ">"
			c.next()
		} else if c.accept("&") {
			c.next()
			c.expect("&")
			output += "&&"
		} else if c.accept("|") {
			c.next()
			c.expect("|")
			output += "||"
		} else if c.accept("=") {
			output += "="
			c.next()
		} else {
			c.next()
			c.expect("=")
			output += "!="
		}

		if c.accept("=") {
			output += "="
			c.next()
		}

		output += c.parseExpression(false)
	}

	if out {
		c.appendOut(output, false)
	}

	return output
}

func (c *Compiler) parseIdentifier() string {
	output := ""

	if c.accept("code") {
		output += c.parseInlineCode()
	} else if c.seek("(") && !c.accept("!") && !c.accept("-") {
		name := c.get().Token
		c.next()
		output = "(" + c.parseFunctionCall(false, name) + ")"
	} else if c.accept("[") {
		output += c.parseArray(false)
	} else if c.seek("[") {
		output += "(" + c.get().Token
		c.next()
		c.expect("[")
		output += " select (" + c.parseExpression(false) + "))"
		c.expect("]")
	} else if c.accept("!") || c.accept("-") {
		output = c.get().Token
		c.next()
		output += c.parseTerm()
	} else {
		output = c.get().Token
		c.next()
	}

	return output
}

func (c *Compiler) parseTerm() string {
	if c.accept("(") {
		c.expect("(")
		output := "(" + c.parseExpression(false) + ")"
		c.expect(")")

		return output
	}

	return c.parseIdentifier()
}

func (c *Compiler) parseFactor() string {
	output := c.parseTerm()

	for c.accept("*") || c.accept("/") { // TODO: modulo?
		if c.accept("*") {
			output += "*"
		} else {
			output += "/"
		}

		c.next()
		output += c.parseExpression(false)
	}

	return output
}

func (c *Compiler) parseArith() string {
	output := c.parseFactor()

	for c.accept("+") || c.accept("-") {
		if c.accept("+") {
			output += "+"
		} else {
			output += "-"
		}

		c.next()
		output += c.parseExpression(false)
	}

	return output
}
<commit_msg>Fixed unary function compile.<commit_after>package parser

import (
	"errors"
	"tokenizer"
	"types"
)

const new_line = "\r\n"

// Parses tokens, validates code to a specific degree
// and writes SQF code into desired location.
func (c *Compiler) Parse(token []tokenizer.Token, prettyPrinting bool) string {
	if !c.initParser(token, prettyPrinting) {
		return ""
	}

	for c.tokenIndex < len(token) {
		c.parseBlock()
	}

	return c.out
}

func (c *Compiler) parseBlock() {
	if c.get().Preprocessor {
		c.parsePreprocessor()
	} else if c.accept("var") {
		c.parseVar()
	} else if c.accept("if") {
		c.parseIf()
	} else if c.accept("while") {
		c.parseWhile()
	} else if c.accept("switch") {
		c.parseSwitch()
	} else if c.accept("for") {
		c.parseFor()
	} else if c.accept("foreach") {
		c.parseForeach()
	} else if c.accept("func") {
		c.parseFunction()
	} else if c.accept("return") {
		c.parseReturn()
	} else if c.accept("try") {
		c.parseTryCatch()
	} else if c.accept("exitwith") {
		c.parseExitWith()
	} else if c.accept("waituntil") {
		c.parseWaitUntil()
	} else if c.accept("case") || c.accept("default") {
		return
	} else {
		c.parseStatement()
	}

	if !c.end() && !c.accept("}") {
		c.parseBlock()
	}
}

func (c *Compiler) parsePreprocessor() {
	// we definitely want a new line before and after
	c.appendOut(new_line+c.get().Token+new_line, false)
	c.next()
}

func (c *Compiler) parseVar() {
	c.expect("var")
	c.appendOut(c.get().Token, false)
	c.next()

	if c.accept("=") {
		c.next()
		c.appendOut(" = ", false)
		c.parseExpression(true)
	}

	c.expect(";")
	c.appendOut(";", true)
}

func (c *Compiler) parseArray(out bool) string {
	output := ""
	c.expect("[")
	output += "["

	if !c.accept("]") {
		output += c.parseExpression(false)

		for c.accept(",") {
			c.next()
			output += "," + c.parseExpression(false)
		}
	}

	c.expect("]")
	output += "]"

	if out {
		c.appendOut(output, false)
	}

	return output
}

func (c *Compiler) parseIf() {
	c.expect("if")
	c.appendOut("if (", false)
	c.parseExpression(true)
	c.appendOut(") then {", true)
	c.expect("{")
	c.parseBlock()
	c.expect("}")

	if c.accept("else") {
		c.next()
		c.expect("{")
		c.appendOut("} else {", true)
		c.parseBlock()
		c.expect("}")
	}

	c.appendOut("};", true)
}

func (c *Compiler) parseWhile() {
	c.expect("while")
	c.appendOut("while {", false)
	c.parseExpression(true)
	c.appendOut("} do {", true)
	c.expect("{")
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseSwitch() {
	c.expect("switch")
	c.appendOut("switch (", false)
	c.parseExpression(true)
	c.appendOut(") do {", true)
	c.expect("{")
	c.parseSwitchBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseSwitchBlock() {
	if c.accept("}") {
		return
	}

	if c.accept("case") {
		c.next()
		c.appendOut("case ", false)
		c.parseExpression(true)
		c.expect(":")
		c.appendOut(":", true)

		if !c.accept("case") && !c.accept("}") && !c.accept("default") {
			c.appendOut("{", true)
			c.parseBlock()
			c.appendOut("};", true)
		}
	} else if c.accept("default") {
		c.next()
		c.expect(":")
		c.appendOut("default:", true)

		if !c.accept("}") {
			c.appendOut("{", true)
			c.parseBlock()
			c.appendOut("};", true)
		}
	}

	c.parseSwitchBlock()
}

func (c *Compiler) parseFor() {
	c.expect("for")
	c.appendOut("for [{", false)

	// var in first assignment is optional
	if c.accept("var") {
		c.next()
	}

	c.parseExpression(true)
	c.expect(";")
	c.appendOut("}, {", false)
	c.parseExpression(true)
	c.expect(";")
	c.appendOut("}, {", false)
	c.parseExpression(true)
	c.appendOut("}] do {", true)
	c.expect("{")
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseForeach() {
	c.expect("foreach")
	element := c.get().Token
	c.next()
	c.expect("=")
	c.expect(">")
	expr := c.parseExpression(false)
	c.expect("{")
	c.appendOut("{", true)
	c.appendOut(element+" = _x;", true)
	c.parseBlock()
	c.expect("}")
	c.appendOut("} forEach ("+expr+");", true)
}

func (c *Compiler) parseFunction() {
	c.expect("func")

	// check for build in function
	if buildin := types.GetFunction(c.get().Token); buildin != nil {
		panic(errors.New(c.get().Token + " is a build in function, choose a different name"))
	}

	c.appendOut(c.get().Token+" = {", true)
	c.next()
	c.expect("(")
	c.parseFunctionParameter()
	c.expect(")")
	c.expect("{")
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseFunctionParameter() {
	// empty parameter list
	if c.accept("{") {
		return
	}

	c.appendOut("params [", false)

	for !c.accept(")") {
		name := c.get().Token
		c.next()

		if c.accept("=") {
			c.next()
			value := c.get().Token
			c.next()
			c.appendOut("[\""+name+"\","+value+"]", false)
		} else {
			c.appendOut("\""+name+"\"", false)
		}

		if !c.accept(")") {
			c.expect(",")
			c.appendOut(",", false)
		}
	}

	c.appendOut("];", true)
}

func (c *Compiler) parseReturn() {
	c.expect("return")
	c.appendOut("return ", false)
	c.parseExpression(true)
	c.expect(";")
	c.appendOut(";", true)
}

func (c *Compiler) parseTryCatch() {
	c.expect("try")
	c.expect("{")
	c.appendOut("try {", true)
	c.parseBlock()
	c.expect("}")
	c.expect("catch")
	c.expect("{")
	c.appendOut("} catch {", true)
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseExitWith() {
	c.expect("exitwith")
	c.expect("{")
	c.appendOut("if (true) exitWith {", true)
	c.parseBlock()
	c.expect("}")
	c.appendOut("};", true)
}

func (c *Compiler) parseWaitUntil() {
	c.expect("waituntil")
	c.expect("(")
	c.appendOut("waitUntil {", false)
	c.parseExpression(true)

	if c.accept(";") {
		c.next()
		c.appendOut(";", false)
		c.parseExpression(true)
	}

	c.expect(")")
	c.expect(";")
	c.appendOut("};", true)
}

func (c *Compiler) parseInlineCode() string {
	c.expect("code")
	c.expect("(")

	code := c.get().Token
	c.next()
	output := "{}"

	if len(code) > 2 {
		compiler := Compiler{}
		output = "{" + compiler.Parse(tokenizer.Tokenize([]byte(code[1:len(code)-1]), true), false) + "}"
	}

	c.expect(")")

	return output
}

// Everything that does not start with a keyword.
func (c *Compiler) parseStatement() {
	// empty block
	if c.accept("}") || c.accept("case") || c.accept("default") {
		return
	}

	// variable or function name
	name := c.get().Token
	c.next()

	if c.accept("=") {
		c.appendOut(name, false)
		c.parseAssignment()
	} else {
		c.parseFunctionCall(true, name)
		c.expect(";")
		c.appendOut(";", true)
	}

	if !c.end() {
		c.parseBlock()
	}
}

func (c *Compiler) parseAssignment() {
	c.expect("=")
	c.appendOut(" = ", false)
	c.parseExpression(true)
	c.expect(";")
	c.appendOut(";", true)
}

func (c *Compiler) parseFunctionCall(out bool, name string) string {
	output := ""

	c.expect("(")
	paramsStr, paramCount := c.parseParameter(false)
	c.expect(")")

	// buildin function
	buildin := types.GetFunction(name)

	if buildin != nil {
		if buildin.Type == types.NULL {
			output = name
		} else if buildin.Type == types.UNARY {
			output = c.parseUnaryFunction(name, paramsStr, paramCount)
		} else {
			output = c.parseBinaryFunction(name, paramsStr, buildin, paramCount)
		}
	} else {
		output = "[" + paramsStr + "] call " + name
	}

	if out {
		c.appendOut(output, false)
	}

	return output
}

func (c *Compiler) parseUnaryFunction(name, paramsStr string, paramCount int) string {
	output := ""

	if paramCount == 1 {
		output = name + " " + paramsStr
	} else {
		output = name + " [" + paramsStr + "]"
	}

	return output
}

func (c *Compiler) parseBinaryFunction(name string, leftParamsStr string, buildin *types.FunctionType, paramCount int) string {
	output := ""

	c.next()
	rightParamsStr, rightParamCount := c.parseParameter(false)
	c.expect(")")

	if paramCount > 1 {
		leftParamsStr = "[" + leftParamsStr + "]"
	}

	if rightParamCount > 1 {
		rightParamsStr = "[" + rightParamsStr + "]"
	}

	if paramCount > 0 {
		output = leftParamsStr + " " + name + " " + rightParamsStr
	} else {
		output = name + " " + rightParamsStr
	}

	return output
}

func (c *Compiler) parseParameter(out bool) (string, int) {
	output := ""
	count := 0

	for !c.accept(")") {
		expr := c.parseExpression(out)
		output += expr
		count++

		if !c.accept(")") {
			c.expect(",")
			output += ", "
		}
	}

	if out {
		c.appendOut(output, false)
	}

	return output, count
}

func (c *Compiler) parseExpression(out bool) string {
	output := c.parseArith()

	for c.accept("<") || c.accept(">") || c.accept("&") || c.accept("|") || c.accept("=") || c.accept("!") {
		if c.accept("<") {
			output += "<"
			c.next()
		} else if c.accept(">") {
			output += ">"
			c.next()
		} else if c.accept("&") {
			c.next()
			c.expect("&")
			output += "&&"
		} else if c.accept("|") {
			c.next()
			c.expect("|")
			output += "||"
		} else if c.accept("=") {
			output += "="
			c.next()
		} else {
			c.next()
			c.expect("=")
			output += "!="
		}

		if c.accept("=") {
			output += "="
			c.next()
		}

		output += c.parseExpression(false)
	}

	if out {
		c.appendOut(output, false)
	}

	return output
}

func (c *Compiler) parseIdentifier() string {
	output := ""

	if c.accept("code") {
		output += c.parseInlineCode()
	} else if c.seek("(") && !c.accept("!") && !c.accept("-") {
		name := c.get().Token
		c.next()
		output = "(" + c.parseFunctionCall(false, name) + ")"
	} else if c.accept("[") {
		output += c.parseArray(false)
	} else if c.seek("[") {
		output += "(" + c.get().Token
		c.next()
		c.expect("[")
		output += " select (" + c.parseExpression(false) + "))"
		c.expect("]")
	} else if c.accept("!") || c.accept("-") {
		output = c.get().Token
		c.next()
		output += c.parseTerm()
	} else {
		output = c.get().Token
		c.next()
	}

	return output
}

func (c *Compiler) parseTerm() string {
	if c.accept("(") {
		c.expect("(")
		output := "(" + c.parseExpression(false) + ")"
		c.expect(")")

		return output
	}

	return c.parseIdentifier()
}

func (c *Compiler) parseFactor() string {
	output := c.parseTerm()

	for c.accept("*") || c.accept("/") { // TODO: modulo?
		if c.accept("*") {
			output += "*"
		} else {
			output += "/"
		}

		c.next()
		output += c.parseExpression(false)
	}

	return output
}

func (c *Compiler) parseArith() string {
	output := c.parseFactor()

	for c.accept("+") || c.accept("-") {
		if c.accept("+") {
			output += "+"
		} else {
			output += "-"
		}

		c.next()
		output += c.parseExpression(false)
	}

	return output
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Thomas Minier. All rights reserved.
// Use of this source code is governed by a MIT License
// license that can be found in the LICENSE file.

// Package sparql provides support for requesting RDF Graphs using SPARQL query language
package sparql

import "github.com/Callidon/joseki/rdf"

// sparqlNode represents a node in a SPARQL query execution plan.
// Each implementation of this interface represents a type of operation executed during a SPARQL request.
//
// When all nodes of SPARQL query execution plan are executed in the correct order, a response to the corresponding request will be produced.
// Package sparql provides several implementations for this interface.
type sparqlNode interface {
	execute() chan rdf.BindingsGroup
	executeWith(group rdf.BindingsGroup) chan rdf.BindingsGroup
	bindingNames() []string
}
<commit_msg>add new struct for sparql orm<commit_after>// Copyright (c) 2016 Thomas Minier. All rights reserved.
// Use of this source code is governed by a MIT License
// license that can be found in the LICENSE file.

package sparql

import "github.com/Callidon/joseki/rdf"

// sparqlNode represents a node in a SPARQL query execution plan.
// Each implementation of this interface represents a type of operation executed during a SPARQL request.
//
// When all nodes of SPARQL query execution plan are executed in the correct order, a response to the corresponding request will be produced.
// Package sparql provides several implementations for this interface.
type sparqlNode interface {
	execute() chan rdf.BindingsGroup
	executeWith(group rdf.BindingsGroup) chan rdf.BindingsGroup
	bindingNames() []string
}
<|endoftext|>