<commit_before>package gohl7

import (
	"errors"
	//temporal debug location
	//"log"
)

var (
	ErrMssgHeader     = errors.New("Invalid Message Header")
	ErrMssgEncoding   = errors.New("Invalid message encoding field")
	ErrUnexpectedCase = errors.New("Unexpected case, implementation error")
	errNoMoreData     = errors.New("no more data")
)

const (
	HEADER_LABEL      = "MSH"
	CR           byte = '\r'
	NL           byte = '\n'
)

type Hl7Parser struct {
	enc  *Encoding
	mssg *Message
}

func NewHl7Parser(source []byte) (*Hl7Parser, error) {

	l := len(source)

	if l < 3 || string(source[:3]) != HEADER_LABEL {
		return nil, ErrMssgHeader
	}

	if l < 8 {
		return nil, ErrMssgEncoding
	}

	enc, err := ParseEncoding(source[3:8])
	if err != nil {
		return nil, err
	}

	segments := NewComplexField(message, MessageValidator)

	return &Hl7Parser{
		enc: enc,
		mssg: &Message{
			raw:          source,
			ComplexField: segments,
		},
	}, nil

}

func (p *Hl7Parser) Parse() (*Message, error) {

	mssg := p.mssg
	currentSegment := NewComplexField(segment, SegmentValidator)

	//manually adding header
	err := currentSegment.Push(NewSimpleField(mssg.raw[:3]))
	if err != nil {
		return nil, err
	}

	//manually adding encoding
	err = currentSegment.Push(NewSimpleField(mssg.raw[4:8]))
	if err != nil {
		return nil, err
	}

	last := Simple
	i, l := 9, len(mssg.raw)

	//the i <= l instead of i < l is to handle wrong end formated files that
	//do not end on \r Ex: MSH|^~\&|~
	for i <= l {
		//temporal debug location
		//log.Printf("len: %d current: %d left %s\n", l, i, mssg.raw[i:])
		nextF, consumed, err := next(mssg.raw[i:], p.enc)
		//temporal debug location
		//log.Printf("len: %d current: %d consumed: %d err: %s\n", l, i, consumed, err)
		if err != nil {

			if err != errNoMoreData {
				return nil, err
			}
			//treat end of data as segment
			nextF = segment
			err = nil

		}

		value := mssg.raw[i : i+consumed]

		i = i + consumed + 1

		switch {
		case last == segment && nextF == Simple:
			//segment header case
			fallthrough

		case last == Simple && nextF == Simple:
			fallthrough

		case last == Simple && nextF == segment:
			//append last part of segment
			err = currentSegment.Push(NewSimpleField(value))

		case last == Simple && nextF == Component:
			//create complex field component
			complexF := NewComplexField(Component, ComponentValidator)
			//push component into segment
			err = currentSegment.Push(complexF)
			//append simple field
			if err == nil {
				complexF.Push(NewSimpleField(value))
			}

		case last == Simple && nextF == Repeated:

			//create complex field component
			complexF := NewComplexField(Repeated, RepeatedValidator)
			//push repeated into segment
			err = currentSegment.Push(complexF)
			//append simple field
			if err == nil {
				complexF.Push(NewSimpleField(value))
			}

		case last == Simple && nextF == SubComponent:

			//create complex field component
			componentF := NewComplexField(Component, ComponentValidator)
			//push component into segment
			currentSegment.Push(componentF)
			//create complex field subcomponent
			subcomponentF := NewComplexField(SubComponent, SubComponentValidator)
			//push subcomponent into component
			componentF.Push(subcomponentF)
			//push simple field into subcomponent
			subcomponentF.Push(NewSimpleField(value))

		case last == Repeated && nextF == segment:
			fallthrough

		case last == Repeated && nextF == Simple:
			fallthrough

		case last == Repeated && nextF == Repeated:
			err = pushChildToLastChild(currentSegment, NewSimpleField(value))

		case last == Repeated && nextF == Component:

			complexF := NewComplexField(Component, ComponentValidator)
			err = complexF.Push(NewSimpleField(value))
			if err == nil {
				err = pushChildToLastChild(currentSegment, complexF)
			}

		case last == Repeated && nextF == SubComponent:

			//create complex field component
			componentF := NewComplexField(Component, ComponentValidator)
			//create complex field subcomponent
			subcomponentF := NewComplexField(SubComponent, SubComponentValidator)
			//push subcomponent into component
			componentF.Push(subcomponentF)
			//push simple field into subcomponent
			subcomponentF.Push(NewSimpleField(value))

			//push new component into Repeated field
			err = pushChildToLastChild(currentSegment, componentF)

		case last == Component && nextF == segment:
			fallthrough

		case last == Component && nextF == Simple:
			fallthrough

		case last == Component && nextF == Component:

			var complexF *ComplexField
			complexF, err = getLastComplexChild(currentSegment)
			repeatedParent := (complexF.Type() == Repeated)

			if repeatedParent {
				err = pushChildToLastChild(complexF, NewSimpleField(value))
			} else {
				err = pushChildToLastChild(currentSegment, NewSimpleField(value))
			}

		case last == Component && nextF == SubComponent:
			//| ~ ^ ^ &
			var complexF *ComplexField

			complexF, err = getLastComplexChild(currentSegment)
			repeatedParent := (complexF.Type() == Repeated)

			if repeatedParent {
				//then the last child has to be the component
				complexF, err = getLastComplexChild(complexF)
			}

			//complex should reference the current component

			//create complex field subcomponent
			subcomponentF := NewComplexField(SubComponent, SubComponentValidator)
			//push subcomponent into component
			complexF.Push(subcomponentF)
			//push simple field into subcomponent
			subcomponentF.Push(NewSimpleField(value))

		case last == Component && nextF == Repeated:

			var complexF *ComplexField

			complexF, err = popLastComplexChild(currentSegment)

			//already a repeated field
			if complexF.Type() == Repeated {
				//put it back
				currentSegment.Push(complexF)
				//then its last child has to be the component
				complexF, err = getLastComplexChild(complexF)

			} else {
				//build a new repeated field
				repeatedF := NewComplexField(Repeated, RepeatedValidator)
				//push existing component to it
				repeatedF.Push(complexF)

				//push repeated field to segment
				currentSegment.Push(repeatedF)

			}
			//push to component the simple field
			complexF.Push(NewSimpleField(value))

		case last == SubComponent && nextF == Simple:
			fallthrough

		case last == SubComponent && nextF == Component:
			fallthrough

		case last == SubComponent && nextF == SubComponent:
			fallthrough

		case last == SubComponent && nextF == segment:

			var complexF *ComplexField

			complexF, err = getLastComplexChild(currentSegment)
			repeatedParent := (complexF.Type() == Repeated)

			if repeatedParent {
				//then the last child has to be the component
				complexF, err = getLastComplexChild(complexF)
			}

			//complexF should reference the current component
			pushChildToLastChild(complexF, NewSimpleField(value))

		case last == SubComponent && nextF == Repeated:

			var complexF *ComplexField
			complexF, err = popLastComplexChild(currentSegment)
			repeatedParent := (complexF.Type() == Repeated)

			//already on repeated field
			if repeatedParent {
				//put it back
				currentSegment.Push(complexF)
				//then the last child has to be the component
				complexF, err = getLastComplexChild(complexF)

			} else {
				//build a new repeated field
				repeatedF := NewComplexField(Repeated, RepeatedValidator)
				//push existing component to it
				repeatedF.Push(complexF)

				//push repeated field to segment
				currentSegment.Push(repeatedF)
			}

			//complexF should reference the current component
			pushChildToLastChild(complexF, NewSimpleField(value))

		case last == segment && nextF == segment:
			//special case for CR+EndOfData
			//noop
		default:
			err = ErrUnexpectedCase
		}

		//case to handle new segments, excluding special case above.
		if err == nil && nextF == segment && last != segment {

			//add current segment to the message
			err = mssg.Push(currentSegment)
			//create new segment
			currentSegment = NewComplexField(segment, SegmentValidator)
		}

		if err != nil {
			return nil, err
		}

		last = nextF

	}

	return mssg, nil
}

func next(source []byte, enc *Encoding) (FieldType, int, error) {

	l, k := len(source), 0

	for ; k < l; k++ {

		v := source[k]
		switch v {
		case enc.Field:
			return Simple, k, nil
		case enc.Component:
			return Component, k, nil
		case enc.Repeated:
			return Repeated, k, nil
		case enc.Subcomponent:
			return SubComponent, k, nil
		case CR:
			return segment, k, nil
		case enc.Escaping:
			//continue
			k++
		}
	}

	return 0, k, errNoMoreData
}

//Given a complex field append a new child to its last child
//in other word: add a grandchild
//in other words: push child to last child
func pushChildToLastChild(parent *ComplexField, newChild Field) error {

	complexF, err := getLastComplexChild(parent)
	if err != nil {
		return err
	}

	//push new child
	return complexF.Push(newChild)
}

func popLastComplexChild(parent *ComplexField) (*ComplexField, error) {

	lastField, err := parent.Pop()
	if err != nil {
		return nil, err
	}

	//converting last field back to *ComplexField
	complexF, ok := lastField.(*ComplexField)
	if !ok {
		return nil, ErrUnexpectedCase
	}

	return complexF, nil

}

//getLastComplexChild get the reference of the last complex child. helper function
func getLastComplexChild(parent *ComplexField) (*ComplexField, error) {

	//pop the child
	child, err := popLastComplexChild(parent)
	if err != nil {
		return child, err
	}

	//push the child back
	return child, parent.Push(child)
}
<commit_msg>Adding all the ugly :) error checks<commit_after>package gohl7

import (
	"errors"
	//temporal debug location
	//"log"
)

var (
	ErrMssgHeader     = errors.New("Invalid Message Header")
	ErrMssgEncoding   = errors.New("Invalid message encoding field")
	ErrUnexpectedCase = errors.New("Unexpected case, implementation error")
	errNoMoreData     = errors.New("no more data")
)

const (
	HEADER_LABEL      = "MSH"
	CR           byte = '\r'
	NL           byte = '\n'
)

type Hl7Parser struct {
	enc  *Encoding
	mssg *Message
}

func NewHl7Parser(source []byte) (*Hl7Parser, error) {

	l := len(source)

	if l < 3 || string(source[:3]) != HEADER_LABEL {
		return nil, ErrMssgHeader
	}

	if l < 8 {
		return nil, ErrMssgEncoding
	}

	enc, err := ParseEncoding(source[3:8])
	if err != nil {
		return nil, err
	}

	segments := NewComplexField(message, MessageValidator)

	return &Hl7Parser{
		enc: enc,
		mssg: &Message{
			raw:          source,
			ComplexField: segments,
		},
	}, nil

}

func (p *Hl7Parser) Parse() (*Message, error) {

	mssg := p.mssg
	currentSegment := NewComplexField(segment, SegmentValidator)

	//manually adding header
	err := currentSegment.Push(NewSimpleField(mssg.raw[:3]))
	if err != nil {
		return nil, err
	}

	//manually adding encoding
	err = currentSegment.Push(NewSimpleField(mssg.raw[4:8]))
	if err != nil {
		return nil, err
	}

	last := Simple
	i, l := 9, len(mssg.raw)

	//the i <= l instead of i < l is to handle wrong end formated files that
	//do not end on \r Ex: MSH|^~\&|~
	for i <= l {
		//temporal debug location
		//log.Printf("len: %d current: %d left %s\n", l, i, mssg.raw[i:])
		nextF, consumed, err := next(mssg.raw[i:], p.enc)
		//temporal debug location
		//log.Printf("len: %d current: %d consumed: %d err: %s\n", l, i, consumed, err)
		if err != nil {

			if err != errNoMoreData {
				return nil, err
			}
			//treat end of data as segment
			nextF = segment
			err = nil

		}

		value := mssg.raw[i : i+consumed]

		i = i + consumed + 1

		switch {
		case last == segment && nextF == Simple:
			//segment header case
			fallthrough

		case last == Simple && nextF == Simple:
			fallthrough

		case last == Simple && nextF == segment:
			//append last part of segment
			err = currentSegment.Push(NewSimpleField(value))

		case last == Simple && nextF == Component:
			//create complex field component
			complexF := NewComplexField(Component, ComponentValidator)
			//push component into segment
			err = currentSegment.Push(complexF)
			//append simple field
			if err == nil {
				complexF.Push(NewSimpleField(value))
			}

		case last == Simple && nextF == Repeated:

			//create complex field component
			complexF := NewComplexField(Repeated, RepeatedValidator)
			//push repeated into segment
			err = currentSegment.Push(complexF)
			//append simple field
			if err == nil {
				complexF.Push(NewSimpleField(value))
			}

		case last == Simple && nextF == SubComponent:

			//create complex field component
			componentF := NewComplexField(Component, ComponentValidator)
			//push component into segment
			err = currentSegment.Push(componentF)
			//create complex field subcomponent
			subcomponentF := NewComplexField(SubComponent, SubComponentValidator)
			//push subcomponent into component
			if err == nil {
				err = componentF.Push(subcomponentF)
			}
			//push simple field into subcomponent
			if err == nil {
				err = subcomponentF.Push(NewSimpleField(value))
			}

		case last == Repeated && nextF == segment:
			fallthrough

		case last == Repeated && nextF == Simple:
			fallthrough

		case last == Repeated && nextF == Repeated:
			err = pushChildToLastChild(currentSegment, NewSimpleField(value))

		case last == Repeated && nextF == Component:

			complexF := NewComplexField(Component, ComponentValidator)
			err = complexF.Push(NewSimpleField(value))
			if err == nil {
				err = pushChildToLastChild(currentSegment, complexF)
			}

		case last == Repeated && nextF == SubComponent:

			//create complex field component
			componentF := NewComplexField(Component, ComponentValidator)
			//create complex field subcomponent
			subcomponentF := NewComplexField(SubComponent, SubComponentValidator)
			//push subcomponent into component
			err = componentF.Push(subcomponentF)
			//push simple field into subcomponent
			if err == nil {
				err = subcomponentF.Push(NewSimpleField(value))
			}
			//push new component into Repeated field
			if err == nil {
				err = pushChildToLastChild(currentSegment, componentF)
			}

		case last == Component && nextF == segment:
			fallthrough

		case last == Component && nextF == Simple:
			fallthrough

		case last == Component && nextF == Component:

			var complexF *ComplexField
			complexF, err = getLastComplexChild(currentSegment)
			if err != nil {
				break
			}

			repeatedParent := (complexF.Type() == Repeated)

			if repeatedParent {
				err = pushChildToLastChild(complexF, NewSimpleField(value))
			} else {
				err = pushChildToLastChild(currentSegment, NewSimpleField(value))
			}

		case last == Component && nextF == SubComponent:

			var complexF *ComplexField

			complexF, err = getLastComplexChild(currentSegment)
			if err != nil {
				break
			}
			repeatedParent := (complexF.Type() == Repeated)

			if repeatedParent {
				//then the last child has to be the component
				complexF, err = getLastComplexChild(complexF)
			}

			//complex should reference the current component

			//create complex field subcomponent
			subcomponentF := NewComplexField(SubComponent, SubComponentValidator)
			//push subcomponent into component
			if err == nil {
				err = complexF.Push(subcomponentF)
			}

			//push simple field into subcomponent
			if err == nil {
				subcomponentF.Push(NewSimpleField(value))
			}

		case last == Component && nextF == Repeated:

			var complexF *ComplexField
			complexF, err = popLastComplexChild(currentSegment)
			if err != nil {
				break
			}

			//already a repeated field
			if complexF.Type() == Repeated {
				//put it back, no need to check for error
				currentSegment.Push(complexF)
				//then its last child has to be the component
				complexF, err = getLastComplexChild(complexF)

			} else {
				//build a new repeated field
				repeatedF := NewComplexField(Repeated, RepeatedValidator)
				//push existing component to it
				err = repeatedF.Push(complexF)

				//push repeated field to segment
				if err == nil {
					err = currentSegment.Push(repeatedF)
				}

			}
			//push to component the simple field
			if err == nil {
				complexF.Push(NewSimpleField(value))
			}

		case last == SubComponent && nextF == Simple:
			fallthrough

		case last == SubComponent && nextF == Component:
			fallthrough

		case last == SubComponent && nextF == SubComponent:
			fallthrough

		case last == SubComponent && nextF == segment:

			var complexF *ComplexField
			complexF, err = getLastComplexChild(currentSegment)
			if err != nil {
				break
			}

			repeatedParent := (complexF.Type() == Repeated)

			if repeatedParent {
				//then the last child has to be the component
				complexF, err = getLastComplexChild(complexF)
			}

			//complexF should reference the current component
			if err == nil {
				err = pushChildToLastChild(complexF, NewSimpleField(value))
			}

		case last == SubComponent && nextF == Repeated:

			var complexF *ComplexField
			complexF, err = popLastComplexChild(currentSegment)
			if err != nil {
				break
			}

			repeatedParent := (complexF.Type() == Repeated)

			//already on repeated field
			if repeatedParent {
				//put it back, no need to check for error
				currentSegment.Push(complexF)
				//then the last child has to be the component
				complexF, err = getLastComplexChild(complexF)

			} else {
				//build a new repeated field
				repeatedF := NewComplexField(Repeated, RepeatedValidator)
				//push existing component to it
				err = repeatedF.Push(complexF)
				//push repeated field to segment
				if err == nil {
					err = currentSegment.Push(repeatedF)
				}
			}

			//complexF should reference the current component
			if err == nil {
				err = pushChildToLastChild(complexF, NewSimpleField(value))
			}

		case last == segment && nextF == segment:
			//special case for CR+EndOfData
			//noop
		default:
			err = ErrUnexpectedCase
		}

		//case to handle new segments, excluding special case above.
		if err == nil && nextF == segment && last != segment {

			//add current segment to the message
			err = mssg.Push(currentSegment)
			//create new segment
			currentSegment = NewComplexField(segment, SegmentValidator)
		}

		if err != nil {
			return nil, err
		}

		last = nextF

	}

	return mssg, nil
}

func next(source []byte, enc *Encoding) (FieldType, int, error) {

	l, k := len(source), 0

	for ; k < l; k++ {

		v := source[k]
		switch v {
		case enc.Field:
			return Simple, k, nil
		case enc.Component:
			return Component, k, nil
		case enc.Repeated:
			return Repeated, k, nil
		case enc.Subcomponent:
			return SubComponent, k, nil
		case CR:
			return segment, k, nil
		case enc.Escaping:
			//continue
			k++
		}
	}

	return 0, k, errNoMoreData
}

//Given a complex field append a new child to its last child
//in other word: add a grandchild
//in other words: push child to last child
func pushChildToLastChild(parent *ComplexField, newChild Field) error {

	complexF, err := getLastComplexChild(parent)
	if err != nil {
		return err
	}

	//push new child
	return complexF.Push(newChild)
}

func popLastComplexChild(parent *ComplexField) (*ComplexField, error) {

	lastField, err := parent.Pop()
	if err != nil {
		return nil, err
	}

	//converting last field back to *ComplexField
	complexF, ok := lastField.(*ComplexField)
	if !ok {
		return nil, ErrUnexpectedCase
	}

	return complexF, nil

}

//getLastComplexChild get the reference of the last complex child. helper function
func getLastComplexChild(parent *ComplexField) (*ComplexField, error) {

	//pop the child
	child, err := popLastComplexChild(parent)
	if err != nil {
		return child, err
	}

	//push the child back
	return child, parent.Push(child)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path"
)

const (
	flagClobber = 1 << iota
	flagForce
	flagVerbose
	flagNoCmds
	flagNoLinks
	flagNoTemplates
	flagNoMacro
	flagUnlink = flagNoCmds | (1 << iota)
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] conf src\n", path.Base(os.Args[0]))
	fmt.Fprintf(os.Stderr, "https://foosoft.net/projects/homemaker/\n\n")
	fmt.Fprintf(os.Stderr, "Parameters:\n")
	flag.PrintDefaults()
}

func main() {
	taskName := flag.String("task", "default", "name of task to execute")
	dstDir := flag.String("dest", os.Getenv("HOME"), "target directory for tasks")
	force := flag.Bool("force", true, "create parent directories to target")
	clobber := flag.Bool("clobber", false, "delete files and directories at target")
	verbose := flag.Bool("verbose", false, "verbose output")
	nocmds := flag.Bool("nocmds", false, "don't execute commands")
	nolinks := flag.Bool("nolinks", false, "don't create links")
	notemplates := flag.Bool("notemplates", false, "don't process templates")
	variant := flag.String("variant", "", "execution variant for tasks and macros")
	unlink := flag.Bool("unlink", false, "remove existing links instead of creating them")

	flag.Usage = usage
	flag.Parse()

	flags := 0
	if *clobber {
		flags |= flagClobber
	}
	if *force {
		flags |= flagForce
	}
	if *verbose {
		flags |= flagVerbose
	}
	if *nocmds {
		flags |= flagNoCmds
	}
	if *nolinks {
		flags |= flagNoLinks
	}
	if *notemplates {
		flags |= flagNoTemplates
	}
	if *unlink {
		flags |= flagUnlink
	}

	if flag.NArg() == 2 {
		confFile := makeAbsPath(flag.Arg(0))

		conf, err := newConfig(confFile)
		if err != nil {
			log.Fatal(err)
		}

		conf.srcDir = makeAbsPath(flag.Arg(1))
		conf.dstDir = makeAbsPath(*dstDir)
		conf.variant = *variant
		conf.flags = flags

		os.Setenv("HM_CONFIG", confFile)
		os.Setenv("HM_TASK", *taskName)
		os.Setenv("HM_SRC", conf.srcDir)
		os.Setenv("HM_DEST", conf.dstDir)
		os.Setenv("HM_VARIANT", conf.variant)

		if err := processTask(*taskName, conf); err != nil {
			log.Fatal(err)
		}
	} else {
		usage()
		os.Exit(2)
	}
}
<commit_msg>Use platform independent os.UserHomeDir() function<commit_after>/*
 * Copyright (c) 2015 Alex Yatskov <alex@foosoft.net>
 * Author: Alex Yatskov <alex@foosoft.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"strings"
)

const (
	flagClobber = 1 << iota
	flagForce
	flagVerbose
	flagNoCmds
	flagNoLinks
	flagNoTemplates
	flagNoMacro
	flagUnlink = flagNoCmds | (1 << iota)
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: %s [options] conf src\n", path.Base(os.Args[0]))
	fmt.Fprintf(os.Stderr, "https://foosoft.net/projects/homemaker/\n\n")
	fmt.Fprintf(os.Stderr, "Parameters:\n")
	flag.PrintDefaults()
}

func main() {
	taskName := flag.String("task", "default", "name of task to execute")
	dstDir := flag.String("dest", "", "target directory for tasks")
	force := flag.Bool("force", true, "create parent directories to target")
	clobber := flag.Bool("clobber", false, "delete files and directories at target")
	verbose := flag.Bool("verbose", false, "verbose output")
	nocmds := flag.Bool("nocmds", false, "don't execute commands")
	nolinks := flag.Bool("nolinks", false, "don't create links")
	notemplates := flag.Bool("notemplates", false, "don't process templates")
	variant := flag.String("variant", "", "execution variant for tasks and macros")
	unlink := flag.Bool("unlink", false, "remove existing links instead of creating them")

	flag.Usage = usage
	flag.Parse()

	flags := 0
	if *clobber {
		flags |= flagClobber
	}
	if *force {
		flags |= flagForce
	}
	if *verbose {
		flags |= flagVerbose
	}
	if *nocmds {
		flags |= flagNoCmds
	}
	if *nolinks {
		flags |= flagNoLinks
	}
	if *notemplates {
		flags |= flagNoTemplates
	}
	if *unlink {
		flags |= flagUnlink
	}

	if flag.NArg() == 2 {
		confFile := makeAbsPath(flag.Arg(0))

		conf, err := newConfig(confFile)
		if err != nil {
			log.Fatal(err)
		}

		if strings.TrimSpace(*dstDir) == "" {
			*dstDir, _ = os.UserHomeDir()
		}

		conf.srcDir = makeAbsPath(flag.Arg(1))
		conf.dstDir = makeAbsPath(*dstDir)
		conf.variant = *variant
		conf.flags = flags

		os.Setenv("HM_CONFIG", confFile)
		os.Setenv("HM_TASK", *taskName)
		os.Setenv("HM_SRC", conf.srcDir)
		os.Setenv("HM_DEST", conf.dstDir)
		os.Setenv("HM_VARIANT", conf.variant)

		if err := processTask(*taskName, conf); err != nil {
			log.Fatal(err)
		}
	} else {
		usage()
		os.Exit(2)
	}
}
<|endoftext|>


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

package hook

import (
	"github.com/tsuru/config"
	"github.com/tsuru/gandalf/fs"
	"io"
	"os"
	"strings"
)

// Adds a hook script.
func Add(name string, body io.ReadCloser) error {
	path, err := config.GetString("git:bare:template")
	if err != nil {
		return err
	}
	s := []string{path, "hooks", name}
	scriptPath := strings.Join(s, "/")
	file, err := fs.Filesystem().OpenFile(scriptPath, os.O_WRONLY|os.O_CREATE, 0755)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = io.Copy(file, body)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>hook: receive just an io.Reader in AddHook<commit_after>// Copyright 2014 gandalf authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package hook

import (
	"github.com/tsuru/config"
	"github.com/tsuru/gandalf/fs"
	"io"
	"os"
	"strings"
)

// Adds a hook script.
func Add(name string, body io.Reader) error {
	path, err := config.GetString("git:bare:template")
	if err != nil {
		return err
	}
	s := []string{path, "hooks", name}
	scriptPath := strings.Join(s, "/")
	file, err := fs.Filesystem().OpenFile(scriptPath, os.O_WRONLY|os.O_CREATE, 0755)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = io.Copy(file, body)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package hook

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

// Constants used to specify the parameter source
const (
	SourceHeader        string = "header"
	SourceQuery         string = "url"
	SourcePayload       string = "payload"
	SourceString        string = "string"
	SourceEntirePayload string = "entire-payload"
	SourceEntireQuery   string = "entire-query"
	SourceEntireHeaders string = "entire-headers"
)

const (
	// EnvNamespace is the prefix used for passing arguments into the command
	// environment.
	EnvNamespace string = "HOOK_"
)

// SignatureError describes an invalid payload signature passed to Hook.
type SignatureError struct {
	Signature string
}

func (e *SignatureError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return fmt.Sprintf("invalid payload signature %s", e.Signature)
}

// ArgumentError describes an invalid argument passed to Hook.
type ArgumentError struct {
	Argument Argument
}

func (e *ArgumentError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return fmt.Sprintf("couldn't retrieve argument for %+v", e.Argument)
}

// SourceError describes an invalid source passed to Hook.
type SourceError struct {
	Argument Argument
}

func (e *SourceError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return fmt.Sprintf("invalid source for argument %+v", e.Argument)
}

// ParseError describes an error parsing user input.
type ParseError struct {
	Err error
}

func (e *ParseError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return e.Err.Error()
}

// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
func CheckPayloadSignature(payload []byte, secret string, signature string) (string, error) {
	if strings.HasPrefix(signature, "sha1=") {
		signature = signature[5:]
	}

	mac := hmac.New(sha1.New, []byte(secret))
	_, err := mac.Write(payload)
	if err != nil {
		return "", err
	}
	expectedMAC := hex.EncodeToString(mac.Sum(nil))

	if !hmac.Equal([]byte(signature), []byte(expectedMAC)) {
		return expectedMAC, &SignatureError{expectedMAC}
	}
	return expectedMAC, err
}

// ReplaceParameter replaces parameter value with the passed value in the passed map
// (please note you should pass pointer to the map, because we're modifying it)
// based on the passed string
func ReplaceParameter(s string, params interface{}, value interface{}) bool {
	if params == nil {
		return false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseUint(p[0], 10, 64)

				if err != nil || paramsValueSliceLength <= int(index) {
					return false
				}

				return ReplaceParameter(p[1], params.([]interface{})[index], value)
			}
		}

		return false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return ReplaceParameter(p[1], pValue, value)
		}
	} else {
		if _, ok := (*params.(*map[string]interface{}))[p[0]]; ok {
			(*params.(*map[string]interface{}))[p[0]] = value
			return true
		}
	}

	return false
}

// GetParameter extracts interface{} value based on the passed string
func GetParameter(s string, params interface{}) (interface{}, bool) {
	if params == nil {
		return nil, false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseUint(p[0], 10, 64)

				if err != nil || paramsValueSliceLength <= int(index) {
					return nil, false
				}

				return GetParameter(p[1], params.([]interface{})[index])
			}

			index, err := strconv.ParseUint(s, 10, 64)

			if err != nil || paramsValueSliceLength <= int(index) {
				return nil, false
			}

			return params.([]interface{})[index], true
		}

		return nil, false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Map {
			if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
				return GetParameter(p[1], pValue)
			}
		} else {
			return nil, false
		}
	} else {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return pValue, true
		}
	}

	return nil, false
}

// ExtractParameterAsString extracts value from interface{} as string based on the passed string
func ExtractParameterAsString(s string, params interface{}) (string, bool) {
	if pValue, ok := GetParameter(s, params); ok {
		return fmt.Sprintf("%v", pValue), true
	}
	return "", false
}

// Argument type specifies the parameter key name and the source it should
// be extracted from
type Argument struct {
	Source string `json:"source"`
	Name   string `json:"name"`
}

// Get Argument method returns the value for the Argument's key name
// based on the Argument's source
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
	var source *map[string]interface{}

	switch ha.Source {
	case SourceHeader:
		source = headers
	case SourceQuery:
		source = query
	case SourcePayload:
		source = payload
	case SourceString:
		return ha.Name, true
	case SourceEntirePayload:
		r, err := json.Marshal(payload)

		if err != nil {
			return "", false
		}

		return string(r), true
	case SourceEntireHeaders:
		r, err := json.Marshal(headers)

		if err != nil {
			return "", false
		}

		return string(r), true
	case SourceEntireQuery:
		r, err := json.Marshal(query)

		if err != nil {
			return "", false
		}

		return string(r), true
	}

	if source != nil {
		return ExtractParameterAsString(ha.Name, *source)
	}

	return "", false
}

// Hook type is a structure containing details for a single hook
type Hook struct {
	ID                       string     `json:"id"`
	ExecuteCommand           string     `json:"execute-command"`
	CommandWorkingDirectory  string     `json:"command-working-directory"`
	ResponseMessage          string     `json:"response-message"`
	CaptureCommandOutput     bool       `json:"include-command-output-in-response"`
	PassEnvironmentToCommand []Argument `json:"pass-environment-to-command"`
	PassArgumentsToCommand   []Argument `json:"pass-arguments-to-command"`
	JSONStringParameters     []Argument `json:"parse-parameters-as-json"`
	TriggerRule              *Rules     `json:"trigger-rule"`
}

// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
// string with the newly created object
func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}) error {
	for i := range h.JSONStringParameters {
		if arg, ok := h.JSONStringParameters[i].Get(headers, query, payload); ok {
			var newArg map[string]interface{}

			decoder := json.NewDecoder(strings.NewReader(string(arg)))
			decoder.UseNumber()

			err := decoder.Decode(&newArg)

			if err != nil {
				return &ParseError{err}
			}

			var source *map[string]interface{}

			switch h.JSONStringParameters[i].Source {
			case SourceHeader:
				source = headers
			case SourcePayload:
				source = payload
			case SourceQuery:
				source = query
			}

			if source != nil {
				ReplaceParameter(h.JSONStringParameters[i].Name, source, newArg)
			} else {
				return &SourceError{h.JSONStringParameters[i]}
			}
		} else {
			return &ArgumentError{h.JSONStringParameters[i]}
		}
	}

	return nil
}

// ExtractCommandArguments creates a list of arguments, based on the
// PassArgumentsToCommand property that is ready to be used with exec.Command()
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) ([]string, error) {
	var args = make([]string, 0)

	args = append(args, h.ExecuteCommand)

	for i := range h.PassArgumentsToCommand {
		if arg, ok := h.PassArgumentsToCommand[i].Get(headers, query, payload); ok {
			args = append(args, arg)
		} else {
			args = append(args, "")
			return args, &ArgumentError{h.PassArgumentsToCommand[i]}
		}
	}

	return args, nil
}

// ExtractCommandArgumentsForEnv creates a list of arguments in key=value
// format, based on the PassEnvironmentToCommand property that is ready to be used
// with exec.Command().
func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string]interface{}) ([]string, error) {
	var args = make([]string, 0)

	for i := range h.PassEnvironmentToCommand {
		if arg, ok := h.PassEnvironmentToCommand[i].Get(headers, query, payload); ok {
			args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
		} else {
			return args, &ArgumentError{h.PassEnvironmentToCommand[i]}
		}
	}

	return args, nil
}

// Hooks is an array of Hook objects
type Hooks []Hook

// LoadFromFile attempts to load hooks from specified JSON file
func (h *Hooks) LoadFromFile(path string) error {
	if path == "" {
		return nil
	}

	// parse hook file for hooks
	file, e := ioutil.ReadFile(path)

	if e != nil {
		return e
	}

	e = json.Unmarshal(file, h)
	return e
}

// Match iterates through Hooks and returns first one that matches the given ID,
// if no hook matches the given ID, nil is returned
func (h *Hooks) Match(id string) *Hook {
	for i := range *h {
		if (*h)[i].ID == id {
			return &(*h)[i]
		}
	}

	return nil
}

// MatchAll iterates through Hooks and returns all of the hooks that match the
// given ID, if no hook matches the given ID, nil is returned
func (h *Hooks) MatchAll(id string) []*Hook {
	var matchedHooks []*Hook
	for i := range *h {
		if (*h)[i].ID == id {
			matchedHooks = append(matchedHooks, &(*h)[i])
		}
	}

	if len(matchedHooks) > 0 {
		return matchedHooks
	}

	return nil
}

// Rules is a structure that contains one of the valid rule types
type Rules struct {
	And   *AndRule   `json:"and"`
	Or    *OrRule    `json:"or"`
	Not   *NotRule   `json:"not"`
	Match *MatchRule `json:"match"`
}

// Evaluate finds the first rule property that is not nil and returns the value
// it evaluates to
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	switch {
	case r.And != nil:
		return r.And.Evaluate(headers, query, payload, body)
	case r.Or != nil:
		return r.Or.Evaluate(headers, query, payload, body)
	case r.Not != nil:
		return r.Not.Evaluate(headers, query, payload, body)
	case r.Match != nil:
		return r.Match.Evaluate(headers, query, payload, body)
	}

	return false, nil
}

// AndRule will evaluate to true if and only if all of the ChildRules evaluate to true
type AndRule []Rules

// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	res := true

	for _, v := range r {
		rv, err := v.Evaluate(headers, query, payload, body)
		if err != nil {
			return false, err
		}

		res = res && rv
		if res == false {
			return res, nil
		}
	}

	return res, nil
}

// OrRule will evaluate to true if any of the ChildRules evaluate to true
type OrRule []Rules

// Evaluate OrRule will return true if any of ChildRules evaluate to true
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	res := false

	for _, v := range r {
		rv, err := v.Evaluate(headers, query, payload, body)
		if err != nil {
			return false, err
		}

		res = res || rv
		if res == true {
			return res, nil
		}
	}

	return res, nil
}

// NotRule will evaluate to true if any and only if the ChildRule evaluates to false
type NotRule Rules

// Evaluate NotRule will return true if and only if ChildRule evaluates to false
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	rv, err := Rules(r).Evaluate(headers, query, payload, body)
	return !rv, err
}

// MatchRule will evaluate to true based on the type
type MatchRule struct {
	Type      string   `json:"type"`
	Regex     string   `json:"regex"`
	Secret    string   `json:"secret"`
	Value     string   `json:"value"`
	Parameter Argument `json:"parameter"`
}

// Constants for the MatchRule type
const (
	MatchValue    string = "value"
	MatchRegex    string = "regex"
	MatchHashSHA1 string = "payload-hash-sha1"
)

// Evaluate MatchRule will return based on the type
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	if arg, ok := r.Parameter.Get(headers, query, payload); ok {
		switch r.Type {
		case MatchValue:
			return arg == r.Value, nil
		case MatchRegex:
			return regexp.MatchString(r.Regex, arg)
		case MatchHashSHA1:
			_, err := CheckPayloadSignature(*body, r.Secret, arg)
			return err == nil, err
		}
	}
	return false, nil
}

// CommandStatusResponse type encapsulates the executed command exit code, message, stdout and stderr
type CommandStatusResponse struct {
	ResponseMessage string `json:"message,omitempty"`
	Output          string `json:"output,omitempty"`
	Error           string `json:"error,omitempty"`
}
<commit_msg>add omitempty to json fields<commit_after>package hook

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

// Constants used to specify the parameter source
const (
	SourceHeader        string = "header"
	SourceQuery         string = "url"
	SourcePayload       string = "payload"
	SourceString        string = "string"
	SourceEntirePayload string = "entire-payload"
	SourceEntireQuery   string = "entire-query"
	SourceEntireHeaders string = "entire-headers"
)

const (
	// EnvNamespace is the prefix used for passing arguments into the command
	// environment.
	EnvNamespace string = "HOOK_"
)

// SignatureError describes an invalid payload signature passed to Hook.
type SignatureError struct {
	Signature string
}

func (e *SignatureError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return fmt.Sprintf("invalid payload signature %s", e.Signature)
}

// ArgumentError describes an invalid argument passed to Hook.
type ArgumentError struct {
	Argument Argument
}

func (e *ArgumentError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return fmt.Sprintf("couldn't retrieve argument for %+v", e.Argument)
}

// SourceError describes an invalid source passed to Hook.
type SourceError struct {
	Argument Argument
}

func (e *SourceError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return fmt.Sprintf("invalid source for argument %+v", e.Argument)
}

// ParseError describes an error parsing user input.
type ParseError struct {
	Err error
}

func (e *ParseError) Error() string {
	if e == nil {
		return "<nil>"
	}
	return e.Err.Error()
}

// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
func CheckPayloadSignature(payload []byte, secret string, signature string) (string, error) {
	if strings.HasPrefix(signature, "sha1=") {
		signature = signature[5:]
	}

	mac := hmac.New(sha1.New, []byte(secret))
	_, err := mac.Write(payload)
	if err != nil {
		return "", err
	}
	expectedMAC := hex.EncodeToString(mac.Sum(nil))

	if !hmac.Equal([]byte(signature), []byte(expectedMAC)) {
		return expectedMAC, &SignatureError{expectedMAC}
	}
	return expectedMAC, err
}

// ReplaceParameter replaces parameter value with the passed value in the passed map
// (please note you should pass pointer to the map, because we're modifying it)
// based on the passed string
func ReplaceParameter(s string, params interface{}, value interface{}) bool {
	if params == nil {
		return false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseUint(p[0], 10, 64)

				if err != nil || paramsValueSliceLength <= int(index) {
					return false
				}

				return ReplaceParameter(p[1], params.([]interface{})[index], value)
			}
		}

		return false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return ReplaceParameter(p[1], pValue, value)
		}
	} else {
		if _, ok := (*params.(*map[string]interface{}))[p[0]]; ok {
			(*params.(*map[string]interface{}))[p[0]] = value
			return true
		}
	}

	return false
}

// GetParameter extracts interface{} value based on the passed string
func GetParameter(s string, params interface{}) (interface{}, bool) {
	if params == nil {
		return nil, false
	}

	if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Slice {
		if paramsValueSliceLength := paramsValue.Len(); paramsValueSliceLength > 0 {

			if p := strings.SplitN(s, ".", 2); len(p) > 1 {
				index, err := strconv.ParseUint(p[0], 10, 64)

				if err != nil || paramsValueSliceLength <= int(index) {
					return nil, false
				}

				return GetParameter(p[1], params.([]interface{})[index])
			}

			index, err := strconv.ParseUint(s, 10, 64)

			if err != nil || paramsValueSliceLength <= int(index) {
				return nil, false
			}

			return params.([]interface{})[index], true
		}

		return nil, false
	}

	if p := strings.SplitN(s, ".", 2); len(p) > 1 {
		if paramsValue := reflect.ValueOf(params); paramsValue.Kind() == reflect.Map {
			if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
				return GetParameter(p[1], pValue)
			}
		} else {
			return nil, false
		}
	} else {
		if pValue, ok := params.(map[string]interface{})[p[0]]; ok {
			return pValue, true
		}
	}

	return nil, false
}

// ExtractParameterAsString extracts value from interface{} as string based on the passed string
func ExtractParameterAsString(s string, params interface{}) (string, bool) {
	if pValue, ok := GetParameter(s, params); ok {
		return fmt.Sprintf("%v", pValue), true
	}
	return "", false
}

// Argument type specifies the parameter key name and the source it should
// be extracted from
type Argument struct {
	Source string `json:"source,omitempty"`
	Name   string `json:"name,omitempty"`
}

// Get Argument method returns the value for the Argument's key name
// based on the Argument's source
func (ha *Argument) Get(headers, query, payload *map[string]interface{}) (string, bool) {
	var source *map[string]interface{}

	switch ha.Source {
	case SourceHeader:
		source = headers
	case SourceQuery:
		source = query
	case SourcePayload:
		source = payload
	case SourceString:
		return ha.Name, true
	case SourceEntirePayload:
		r, err := json.Marshal(payload)

		if err != nil {
			return "", false
		}

		return string(r), true
	case SourceEntireHeaders:
		r, err := json.Marshal(headers)

		if err != nil {
			return "", false
		}

		return string(r), true
	case SourceEntireQuery:
		r, err := json.Marshal(query)

		if err != nil {
			return "", false
		}

		return string(r), true
	}

	if source != nil {
		return ExtractParameterAsString(ha.Name, *source)
	}

	return "", false
}

// Hook type is a structure containing details for a single hook
type Hook struct {
	ID                       string     `json:"id,omitempty"`
	ExecuteCommand           string     `json:"execute-command,omitempty"`
	CommandWorkingDirectory  string     `json:"command-working-directory,omitempty"`
	ResponseMessage          string     `json:"response-message,omitempty"`
	CaptureCommandOutput     bool       `json:"include-command-output-in-response,omitempty"`
	PassEnvironmentToCommand []Argument `json:"pass-environment-to-command,omitempty"`
	PassArgumentsToCommand   []Argument `json:"pass-arguments-to-command,omitempty"`
	JSONStringParameters     []Argument `json:"parse-parameters-as-json,omitempty"`
	TriggerRule              *Rules     `json:"trigger-rule,omitempty"`
}

// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
// string with the newly created object
func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}) error {
	for i := range h.JSONStringParameters {
		if arg, ok := h.JSONStringParameters[i].Get(headers, query, payload); ok {
			var newArg map[string]interface{}

			decoder := json.NewDecoder(strings.NewReader(string(arg)))
			decoder.UseNumber()

			err := decoder.Decode(&newArg)

			if err != nil {
				return &ParseError{err}
			}

			var source *map[string]interface{}

			switch h.JSONStringParameters[i].Source {
			case SourceHeader:
				source = headers
			case SourcePayload:
				source = payload
			case SourceQuery:
				source = query
			}

			if source != nil {
				ReplaceParameter(h.JSONStringParameters[i].Name, source, newArg)
			} else {
				return &SourceError{h.JSONStringParameters[i]}
			}
		} else {
			return &ArgumentError{h.JSONStringParameters[i]}
		}
	}

	return nil
}

// ExtractCommandArguments creates a list of arguments, based on the
// PassArgumentsToCommand property that is ready to be used with exec.Command()
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}) ([]string, error) {
	var args = make([]string, 0)

	args = append(args, h.ExecuteCommand)

	for i := range h.PassArgumentsToCommand {
		if arg, ok := h.PassArgumentsToCommand[i].Get(headers, query, payload); ok {
			args = append(args, arg)
		} else {
			args = append(args, "")
			return args, &ArgumentError{h.PassArgumentsToCommand[i]}
		}
	}

	return args, nil
}

// ExtractCommandArgumentsForEnv creates a list of arguments in key=value
// format, based on the PassEnvironmentToCommand property that is ready to be used
// with exec.Command().
func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string]interface{}) ([]string, error) {
	var args = make([]string, 0)

	for i := range h.PassEnvironmentToCommand {
		if arg, ok := h.PassEnvironmentToCommand[i].Get(headers, query, payload); ok {
			args = append(args, EnvNamespace+h.PassEnvironmentToCommand[i].Name+"="+arg)
		} else {
			return args, &ArgumentError{h.PassEnvironmentToCommand[i]}
		}
	}

	return args, nil
}

// Hooks is an array of Hook objects
type Hooks []Hook

// LoadFromFile attempts to load hooks from specified JSON file
func (h *Hooks) LoadFromFile(path string) error {
	if path == "" {
		return nil
	}

	// parse hook file for hooks
	file, e := ioutil.ReadFile(path)

	if e != nil {
		return e
	}

	e = json.Unmarshal(file, h)
	return e
}

// Match iterates through Hooks and returns first one that matches the given ID,
// if no hook matches the given ID, nil is returned
func (h *Hooks) Match(id string) *Hook {
	for i := range *h {
		if (*h)[i].ID == id {
			return &(*h)[i]
		}
	}

	return nil
}

// MatchAll iterates through Hooks and returns all of the hooks that match the
// given ID, if no hook matches the given ID, nil is returned
func (h *Hooks) MatchAll(id string) []*Hook {
	var matchedHooks []*Hook
	for i := range *h {
		if (*h)[i].ID == id {
			matchedHooks = append(matchedHooks, &(*h)[i])
		}
	}

	if len(matchedHooks) > 0 {
		return matchedHooks
	}

	return nil
}

// Rules is a structure that contains one of the valid rule types
type Rules struct {
	And   *AndRule   `json:"and,omitempty"`
	Or    *OrRule    `json:"or,omitempty"`
	Not   *NotRule   `json:"not,omitempty"`
	Match *MatchRule `json:"match,omitempty"`
}

// Evaluate finds the first rule property that is not nil and returns the value
// it evaluates to
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	switch {
	case r.And != nil:
		return r.And.Evaluate(headers, query, payload, body)
	case r.Or != nil:
		return r.Or.Evaluate(headers, query, payload, body)
	case r.Not != nil:
		return r.Not.Evaluate(headers, query, payload, body)
	case r.Match != nil:
		return r.Match.Evaluate(headers, query, payload, body)
	}

	return false, nil
}

// AndRule will evaluate to true if and only if all of the ChildRules evaluate to true
type AndRule []Rules

// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	res := true

	for _, v := range r {
		rv, err := v.Evaluate(headers, query, payload, body)
		if err != nil {
			return false, err
		}

		res = res && rv
		if res == false {
			return res, nil
		}
	}

	return res, nil
}

// OrRule will evaluate to true if any of the ChildRules evaluate to true
type OrRule []Rules

// Evaluate OrRule will return true if any of ChildRules evaluate to true
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	res := false

	for _, v := range r {
		rv, err := v.Evaluate(headers, query, payload, body)
		if err != nil {
			return false, err
		}

		res = res || rv
		if res == true {
			return res, nil
		}
	}

	return res, nil
}

// NotRule will evaluate to true if any and only if the ChildRule evaluates to false
type NotRule Rules

// Evaluate NotRule will return true if and only if ChildRule evaluates to false
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	rv, err := Rules(r).Evaluate(headers, query, payload, body)
	return !rv, err
}

// MatchRule will evaluate to true based on the type
type MatchRule struct {
	Type      string   `json:"type,omitempty"`
	Regex     string   `json:"regex,omitempty"`
	Secret    string   `json:"secret,omitempty"`
	Value     string   `json:"value,omitempty"`
	Parameter Argument `json:"parameter,omitempty"`
}

// Constants for the MatchRule type
const (
	MatchValue    string = "value"
	MatchRegex    string = "regex"
	MatchHashSHA1 string = "payload-hash-sha1"
)

// Evaluate MatchRule will return based on the type
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
	if arg, ok := r.Parameter.Get(headers, query, payload); ok {
		switch r.Type {
		case MatchValue:
			return arg == r.Value, nil
		case MatchRegex:
			return regexp.MatchString(r.Regex, arg)
		case MatchHashSHA1:
			_, err := CheckPayloadSignature(*body, r.Secret, arg)
			return err == nil, err
		}
	}
	return false, nil
}

// CommandStatusResponse type encapsulates the executed command exit code, message, stdout and stderr
type CommandStatusResponse struct {
	ResponseMessage string `json:"message,omitempty"`
	Output          string `json:"output,omitempty"`
	Error           string `json:"error,omitempty"`
}
<|endoftext|>


<commit_before>package argparse

import (
	"testing"
	"errors"
)

func no_exit (exit_status int) {
	panic(exit_status)
}

func TestParse (t *testing.T) {
	exitFunc = no_exit
	var parser *ArgumentParser = New("pouet")
	parser.Option('b', "by", "By", 1, Store, "", "By")
	var	pargs struct {
		By string
	}

	parse := func (v interface{}, s []string) (err error) {
		defer func () {
			if x := recover(); nil != x {
				err = errors.New("panic caught")
			}
		}()
		return parser.ParseArgs(v, s)
	}
	testokfunc := func (a []string) {
		err := parse(&pargs, a)
		if nil != err {
			t.Error("Can not parse cmdline", a, err)
		}
	}
	testnokfunc := func (a []string) {
		err := parse(&pargs, a)
		if nil == err {
			t.Error("Can not parse cmdline", a, err)
		}
	}

	testokfunc([]string{"-b", "pouet"})
	testokfunc([]string{})
	testokfunc([]string{"--", "--truc"})
	testokfunc([]string{"--", "-t"})
	testnokfunc([]string{"-t"})
	testnokfunc([]string{"--truc"})
}

<commit_msg>go fmt applied<commit_after>package argparse

import (
	"errors"
	"testing"
)

func no_exit(exit_status int) {
	panic(exit_status)
}

func TestParse(t *testing.T) {
	exitFunc = no_exit
	var parser *ArgumentParser = New("pouet")
	parser.Option('b', "by", "By", 1, Store, "", "By")
	var pargs struct {
		By string
	}

	parse := func(v interface{}, s []string) (err error) {
		defer func() {
			if x := recover(); nil != x {
				err = errors.New("panic caught")
			}
		}()
		return parser.ParseArgs(v, s)
	}
	testokfunc := func(a []string) {
		err := parse(&pargs, a)
		if nil != err {
			t.Error("Can not parse cmdline", a, err)
		}
	}
	testnokfunc := func(a []string) {
		err := parse(&pargs, a)
		if nil == err {
			t.Error("Can not parse cmdline", a, err)
		}
	}

	testokfunc([]string{"-b", "pouet"})
	testokfunc([]string{})
	testokfunc([]string{"--", "--truc"})
	testokfunc([]string{"--", "-t"})
	testnokfunc([]string{"-t"})
	testnokfunc([]string{"--truc"})
}
<|endoftext|>


<commit_before>
package main

import "fmt"
import "time"

func main() {

    channel := make(chan string)

    go player("A", true, channel)
    go player("B", false, channel)

    var line string
    fmt.Scanln(&line)
}

// each player function runs on its own thread and use the channel to
// exchange a token back and forth
func player(name string, starts bool, channel chan string) {

    iHaveTheToken := starts

    //
    // we go in a loop and exchange the token
    //
    for {

        if iHaveTheToken {

            //
            // put it on the channel
            //

            fmt.Println(name + " writing the token on the channel ...")
            channel <- "."

        } else {

            //
            // wait to get the token
            //

            _ = <- channel
            fmt.Println(name + " read the token from the channel")
            fmt.Println()

            sleep()
        }

        iHaveTheToken = !iHaveTheToken
    }
}

func sleep() {
    time.Sleep(2 * time.Second)
}
<commit_msg>simpler code<commit_after>
package main

import "fmt"
import "time"

func main() {

    channel := make(chan string)

    go player("A", true, channel)
    go player("B", false, channel)

    var line string
    fmt.Scanln(&line)
}

// each player function runs on its own thread and use the channel to
// exchange a token back and forth
func player(name string, iHaveTheToken bool, channel chan string) {

    //
    // we go in a loop and exchange the token
    //
    for {

        if iHaveTheToken {

            //
            // put it on the channel
            //

            fmt.Println(name + " writing the token on the channel ...")
            channel <- "."

        } else {

            //
            // wait to get the token
            //

            _ = <- channel
            fmt.Println(name + " read the token from the channel")
            fmt.Println()

            sleep()
        }

        iHaveTheToken = !iHaveTheToken
    }
}

func sleep() {
    time.Sleep(2 * time.Second)
}
<|endoftext|>


<commit_before>// +build darwin

package filemonitor

import "github.com/fsnotify/fsevents"

const flagsWorthReloadingFor = fsevents.ItemRemoved | fsevents.ItemModified | fsevents.ItemRenamed

type fsEventsMonitor struct {
	fileMonitor
	stream *fsevents.EventStream
	add    chan string
	stop   chan struct{}
}

func NewFileMonitor() (FileMonitor, error) {
	f := fsEventsMonitor{
		stream: &fsevents.EventStream{
			Paths:   []string{},
			Latency: FileChangeDelay,
			Flags:   fsevents.FileEvents,
			EventID: fsevents.EventIDSinceNow,
		},
		// Restarting FSEvents can take ~100ms so buffer adds
		// in the channel so they can be grouped together.
		add:  make(chan string, 5000),
		stop: make(chan struct{}),
	}

	go f.handleAdd()

	return &f, nil
}

func (f *fsEventsMonitor) Add(file string) error {
	f.add <- file
	return nil
}

func (f *fsEventsMonitor) Close() error {
	select {
	case <-f.stop:
		return nil // Already stopped
	default:
		close(f.stop)
		close(f.add)
	}

	return nil
}

func (f *fsEventsMonitor) watch() {
	for {
		select {
		case events := <-f.stream.Events:
			paths := make([]string, 0, len(events))
			for _, event := range events {
				if (event.Flags & (fsevents.ItemIsFile | flagsWorthReloadingFor)) == 0 {
					continue
				}

				paths = append(paths, event.Path)
			}

			if len(paths) == 0 {
				continue
			}

			for _, l := range f.listeners {
				l <- paths
			}
		case <-f.stop:
			return
		}
	}
}

func (f *fsEventsMonitor) handleAdd() {
	watched := make(map[string]bool)
	started := false

	for file := range f.add {
		if watched[file] {
			continue
		}

		allFiles := []string{file}

		// Read all messages waiting in the channel so we can batch restarts
		done := false
		for !done {
			select {
			case file := <-f.add:
				if watched[file] {
					continue
				}

				allFiles = append(allFiles, file)
			default:
				done = true
			}
		}

		for _, file := range allFiles {
			f.stream.Paths = append(f.stream.Paths, file)
		}

		if started {
			f.stream.Restart()
		} else {
			f.stream.Start()
			go f.watch()
			started = true
		}
	}

	if started {
		f.stream.Stop()
	}
}
<commit_msg>Fix adding files to filemonitor under OS X<commit_after>// +build darwin

package filemonitor

import "github.com/fsnotify/fsevents"

const flagsWorthReloadingFor = fsevents.ItemRemoved | fsevents.ItemModified | fsevents.ItemRenamed

type fsEventsMonitor struct {
	fileMonitor
	stream *fsevents.EventStream
	add    chan string
	stop   chan struct{}
}

func NewFileMonitor() (FileMonitor, error) {
	f := fsEventsMonitor{
		stream: &fsevents.EventStream{
			Paths:   []string{},
			Latency: FileChangeDelay,
			Flags:   fsevents.FileEvents,
			EventID: fsevents.EventIDSinceNow,
		},
		// Restarting FSEvents can take ~100ms so buffer adds
		// in the channel so they can be grouped together.
		add:  make(chan string, 5000),
		stop: make(chan struct{}),
	}

	go f.handleAdd()

	return &f, nil
}

func (f *fsEventsMonitor) Add(file string) error {
	f.add <- file
	return nil
}

func (f *fsEventsMonitor) Close() error {
	select {
	case <-f.stop:
		return nil // Already stopped
	default:
		close(f.stop)
		close(f.add)
	}

	return nil
}

func (f *fsEventsMonitor) watch() {
	for {
		select {
		case events := <-f.stream.Events:
			paths := make([]string, 0, len(events))
			for _, event := range events {
				if (event.Flags & (fsevents.ItemIsFile | flagsWorthReloadingFor)) == 0 {
					continue
				}

				paths = append(paths, event.Path)
			}

			if len(paths) == 0 {
				continue
			}

			for _, l := range f.listeners {
				l <- paths
			}
		case <-f.stop:
			return
		}
	}
}

func (f *fsEventsMonitor) handleAdd() {
	watched := make(map[string]bool)
	started := false

	for file := range f.add {
		if watched[file] {
			continue
		}

		allFiles := []string{file}

		// Read all messages waiting in the channel so we can batch restarts
		done := false
		for !done {
			select {
			case file := <-f.add:
				if watched[file] {
					continue
				}

				allFiles = append(allFiles, file)
			default:
				done = true
			}
		}

		for _, file := range allFiles {
			watched[file] = true
			f.stream.Paths = append(f.stream.Paths, file)
		}

		if started {
			f.stream.Restart()
		} else {
			f.stream.Start()
			go f.watch()
			started = true
		}
	}

	if started {
		f.stream.Stop()
	}
}
<|endoftext|>


<commit_before>package statsd

import (
	"fmt"
	client "github.com/etsy/statsd"
	"koding/tools/config"
	"time"
)

var (
	STATSD   = client.New(config.Current.Statsd.Ip, config.Current.Statsd.Port)
	APP_NAME string
)

func SetAppName(name string) {
	APP_NAME = name
}

func Increment(name string) {
	STATSD.Increment(name)
}

func Decrement(name string) {
	STATSD.Decrement(name)
}

type StatdsTimer struct {
	StartTime, EndTime time.Time
	Name               string
}

func StartTimer(name string) *StatdsTimer {
	return &StatdsTimer{
		StartTime: time.Now(),
		Name:      name,
	}
}

func (s *StatdsTimer) Success() {
	s.End("success")
}

func (s *StatdsTimer) Failed() {
	s.End("failed")
}

func (s *StatdsTimer) End(status string) {
	s.EndTime = time.Now()
	duration := int64(s.EndTime.Sub(s.StartTime) / time.Millisecond)
	name := buildName(s.Name, status)
	STATSD.Timing(name, duration)

	Increment(name)
}

func buildName(eventName, status string) string {
	return fmt.Sprintf("koding.%v.%v.%v", APP_NAME, eventName, status)
}
<commit_msg>statsd: add comments<commit_after>// A tiny wrapper around the offical etsy statd package.
//
// Example:
//    statsd.SetAppName("myWorker")
//
//    // from inside a function
//    sTimer := statsd.StartTimer("sendRequest")
//    err := doWork()
//    if err != nil {
//      s.Timer.Failed()
//      return err
//    }
//
//    sTimer.Success()
package statsd

import (
	"fmt"
	client "github.com/etsy/statsd"
	"koding/tools/config"
	"time"
)

var (
	STATSD   = client.New(config.Current.Statsd.Ip, config.Current.Statsd.Port)
	APP_NAME string
)

// App name is used as a namespace, usually set to the name of
// the worker that uses this package.
func SetAppName(name string) {
	APP_NAME = name
}

func Increment(name string) {
	STATSD.Increment(name)
}

func Decrement(name string) {
	STATSD.Decrement(name)
}

type StatdsTimer struct {
	StartTime, EndTime time.Time
	Name               string
}

func StartTimer(name string) *StatdsTimer {
	return &StatdsTimer{
		StartTime: time.Now(),
		Name:      name,
	}
}

func (s *StatdsTimer) Success() {
	s.End("success")
}

func (s *StatdsTimer) Failed() {
	s.End("failed")
}

// Sends timer of event and also increments event by 1.
//
// Events name follow the pattern:
//    koding.<app name>.<event name>.<status>
// Ex:
//    koding.myWorker.work.success
//    koding.myWorker.work.failure
//
// Success() and Failure() are helper methods that adds respective
// status to event name automatically.
func (s *StatdsTimer) End(status string) {
	s.EndTime = time.Now()
	duration := int64(s.EndTime.Sub(s.StartTime) / time.Millisecond)
	name := buildName(s.Name, status)
	STATSD.Timing(name, duration)

	Increment(name)
}

func buildName(eventName, status string) string {
	return fmt.Sprintf("koding.%v.%v.%v", APP_NAME, eventName, status)
}
<|endoftext|>


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

// Package sqlannotation provides functions
// for annotating DML statements with keyspace-id
// comments and parsing them. These annotations
// are used during filtered-replication to route
// the DML statement to the correct shard.
// TOOD(erez): Move the code for the "_stream" annotations
// from vttablet to here.
package sqlannotation

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

const (
	filteredReplicationUnfriendlyComment = "/* vtgate:: filtered_replication_unfriendly */"
	insertDML                            = "insert"
	updateDML                            = "update"
	deleteDML                            = "delete"
)

// AddIfDML annotates 'sql' based on
// the keyspaceIDs found in 'keyspaceIDs':
//
// If 'sql' is not a DML statement no annotation is added.
// If 'sql' is a DML statement and contains exactly one element
// it is used to annotate 'sql'; otherwise 'sql' is annotated
// as replication-unfriendly.
func AddIfDML(sql string, keyspaceIDs [][]byte) string {
	if len(keyspaceIDs) == 1 {
		return AddKeyspaceIDIfDML(sql, keyspaceIDs[0])
	}
	return AddFilteredReplicationUnfriendlyIfDML(sql)
}

// AddKeyspaceIDIfDML returns a copy of 'sql' annotated
// with the given keyspace id if 'sql' is a DML statement;
// otherwise, it returns a copy of 'sql'.
func AddKeyspaceIDIfDML(sql string, keyspaceID []byte) string {
	if isDml(sql) {
		return AddKeyspaceID(sql, keyspaceID)
	}
	return sql
}

// AddKeyspaceID returns a copy of 'sql' annotated
// with the given keyspace id.
func AddKeyspaceID(sql string, keyspaceID []byte) string {
	return fmt.Sprintf("%s /* vtgate:: keyspace_id:%s */",
		sql, hex.EncodeToString(keyspaceID))
}

// AddFilteredReplicationUnfriendlyIfDML annotates the given 'sql'
// query as filtered-replication-unfriendly if its a DML statement
// (filtered-replication should halt if it encounters such an annotation).
// Does nothing if the statement is not a DML statement.
func AddFilteredReplicationUnfriendlyIfDML(sql string) string {
	if isDml(sql) {
		return AddFilteredReplicationUnfriendly(sql)
	}
	return sql
}

// AddFilteredReplicationUnfriendly annotates the given 'sql'
// query as filtered-replication-unfriendly.
func AddFilteredReplicationUnfriendly(sql string) string {
	return sql + filteredReplicationUnfriendlyComment
}

// Copied from go/vt/vtgate/resolver.go
// TODO(erez): Refactor this.

// Returns true if 'querySQL' is an INSERT, UPDATE or DELETE
// statement.
func isDml(querySQL string) bool {
	var sqlKW string
	if i := strings.Index(querySQL, " "); i >= 0 {
		sqlKW = querySQL[:i]
	}
	sqlKW = strings.ToLower(sqlKW)
	if sqlKW == insertDML || sqlKW == updateDML || sqlKW == deleteDML {
		return true
	}
	return false
}

// ExtractKeySpaceID parses the annotation of the given statement and tries
// to extract the keyspace id.
// If a keyspace-id comment exists 'keyspaceID' is set to the parsed keyspace id
// and err is set to nil; otherwise, if a filtered-replication-unfriendly comment exists
// or some other parsing error occured, keyspaceID is set to nil and err is set to a non-nil
// error value.
func ExtractKeySpaceID(sql string) (keyspaceID []byte, err error) {
	keyspaceIDString, hasKeySpaceID := extractStringBetween(sql, "/* vtgate:: keyspace_id:", " ")
	hasUnfriendlyAnnotation := (strings.Index(sql, filteredReplicationUnfriendlyComment) != -1)
	err = nil
	if hasKeySpaceID {
		if hasUnfriendlyAnnotation {
			keyspaceID = nil
			err = &ExtractKeySpaceIDError{
				Kind:    ExtractKeySpaceIDParseError,
				Message: fmt.Sprintf("Conflicting annotations in statement '%v'", sql),
			}
			return
		}
		keyspaceID, err = hex.DecodeString(keyspaceIDString)
		if err != nil {
			keyspaceID = nil
			err = &ExtractKeySpaceIDError{
				Kind: ExtractKeySpaceIDParseError,
				Message: fmt.Sprintf(
					"Error parsing keyspace id value in statement: %v (%v)", sql, err),
			}
		}
		return
	}

	if hasUnfriendlyAnnotation {
		err = &ExtractKeySpaceIDError{
			Kind:    ExtractKeySpaceIDReplicationUnfriendlyError,
			Message: fmt.Sprintf("Statement: %v", sql),
		}
		keyspaceID = nil
		return
	}

	// No annotations.
	keyspaceID = nil
	err = &ExtractKeySpaceIDError{
		Kind:    ExtractKeySpaceIDParseError,
		Message: fmt.Sprintf("No annotation found in '%v'", sql),
	}
	return
}

// Extracts the string from source contained between the leftmost instance of
// 'leftDelim' and the next instance of 'rightDelim'. If there is no next instance
// of 'rightDelim', returns the string contained between the end of the leftmost instance
// of 'leftDelim' to the end of 'source'. If 'leftDelim' does not appear in 'source',
// sets 'found' to false and 'match' to the empty string, otherwise 'found' is set to true
// and 'match' is set to the extracted string.
func extractStringBetween(source string, leftDelim string, rightDelim string) (match string, found bool) {
	leftDelimStart := strings.Index(source, leftDelim)
	if leftDelimStart == -1 {
		found = false
		match = ""
		return
	}
	found = true
	matchStart := leftDelimStart + len(leftDelim)
	matchEnd := strings.Index(source[matchStart:], rightDelim)
	if matchEnd != -1 {
		match = source[matchStart : matchStart+matchEnd]
		return
	}
	match = source[matchStart:]
	return
}

// ExtractKeySpaceIDError is the error type returned
// from ExtractKeySpaceID
// Kind is a numeric code for the error (see constants below)
// and Message is an error message string.
type ExtractKeySpaceIDError struct {
	Kind    int
	Message string
}

// Possible values for ExtractKeySpaceIDError.Kind
const (
	ExtractKeySpaceIDParseError                 = iota
	ExtractKeySpaceIDReplicationUnfriendlyError = iota
)

func (err ExtractKeySpaceIDError) Error() string {
	switch err.Kind {
	case ExtractKeySpaceIDParseError:
		return fmt.Sprintf("Parse-Error. %v", err.Message)
	case ExtractKeySpaceIDReplicationUnfriendlyError:
		return fmt.Sprintf(
			"Statement is filtered-replication-unfriendly. %v", err.Message)
	default:
		log.Fatalf("Unknown error type: %v", err)
		return "" // Unreachable.
	}
}
<commit_msg>Adding a stat counting the number of unfriendly DML statements.<commit_after>// Copyright 2015, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package sqlannotation provides functions
// for annotating DML statements with keyspace-id
// comments and parsing them. These annotations
// are used during filtered-replication to route
// the DML statement to the correct shard.
// TOOD(erez): Move the code for the "_stream" annotations
// from vttablet to here.
package sqlannotation

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

	"github.com/youtube/vitess/go/stats"
)

const (
	filteredReplicationUnfriendlyComment = "/* vtgate:: filtered_replication_unfriendly */"
	insertDML                            = "insert"
	updateDML                            = "update"
	deleteDML                            = "delete"
)

var (
	replicationUnfriendlyCountStat = stats.NewInt("replication-unfriendly-statements-count")
)

// AddIfDML annotates 'sql' based on
// the keyspaceIDs found in 'keyspaceIDs':
//
// If 'sql' is not a DML statement no annotation is added.
// If 'sql' is a DML statement and contains exactly one element
// it is used to annotate 'sql'; otherwise 'sql' is annotated
// as replication-unfriendly.
func AddIfDML(sql string, keyspaceIDs [][]byte) string {
	if len(keyspaceIDs) == 1 {
		return AddKeyspaceIDIfDML(sql, keyspaceIDs[0])
	}
	return AddFilteredReplicationUnfriendlyIfDML(sql)
}

// AddKeyspaceIDIfDML returns a copy of 'sql' annotated
// with the given keyspace id if 'sql' is a DML statement;
// otherwise, it returns a copy of 'sql'.
func AddKeyspaceIDIfDML(sql string, keyspaceID []byte) string {
	if isDml(sql) {
		return AddKeyspaceID(sql, keyspaceID)
	}
	return sql
}

// AddKeyspaceID returns a copy of 'sql' annotated
// with the given keyspace id.
func AddKeyspaceID(sql string, keyspaceID []byte) string {
	return fmt.Sprintf("%s /* vtgate:: keyspace_id:%s */",
		sql, hex.EncodeToString(keyspaceID))
}

// AddFilteredReplicationUnfriendlyIfDML annotates the given 'sql'
// query as filtered-replication-unfriendly if its a DML statement
// (filtered-replication should halt if it encounters such an annotation).
// Does nothing if the statement is not a DML statement.
func AddFilteredReplicationUnfriendlyIfDML(sql string) string {
	if isDml(sql) {
		return AddFilteredReplicationUnfriendly(sql)
	}
	return sql
}

// AddFilteredReplicationUnfriendly annotates the given 'sql'
// query as filtered-replication-unfriendly.
func AddFilteredReplicationUnfriendly(sql string) string {
	replicationUnfriendlyCountStat.Add(1)
	return sql + filteredReplicationUnfriendlyComment
}

// Copied from go/vt/vtgate/resolver.go
// TODO(erez): Refactor this.

// Returns true if 'querySQL' is an INSERT, UPDATE or DELETE
// statement.
func isDml(querySQL string) bool {
	var sqlKW string
	if i := strings.Index(querySQL, " "); i >= 0 {
		sqlKW = querySQL[:i]
	}
	sqlKW = strings.ToLower(sqlKW)
	if sqlKW == insertDML || sqlKW == updateDML || sqlKW == deleteDML {
		return true
	}
	return false
}

// ExtractKeySpaceID parses the annotation of the given statement and tries
// to extract the keyspace id.
// If a keyspace-id comment exists 'keyspaceID' is set to the parsed keyspace id
// and err is set to nil; otherwise, if a filtered-replication-unfriendly comment exists
// or some other parsing error occured, keyspaceID is set to nil and err is set to a non-nil
// error value.
func ExtractKeySpaceID(sql string) (keyspaceID []byte, err error) {
	keyspaceIDString, hasKeySpaceID := extractStringBetween(sql, "/* vtgate:: keyspace_id:", " ")
	hasUnfriendlyAnnotation := (strings.Index(sql, filteredReplicationUnfriendlyComment) != -1)
	err = nil
	if hasKeySpaceID {
		if hasUnfriendlyAnnotation {
			keyspaceID = nil
			err = &ExtractKeySpaceIDError{
				Kind:    ExtractKeySpaceIDParseError,
				Message: fmt.Sprintf("Conflicting annotations in statement '%v'", sql),
			}
			return
		}
		keyspaceID, err = hex.DecodeString(keyspaceIDString)
		if err != nil {
			keyspaceID = nil
			err = &ExtractKeySpaceIDError{
				Kind: ExtractKeySpaceIDParseError,
				Message: fmt.Sprintf(
					"Error parsing keyspace id value in statement: %v (%v)", sql, err),
			}
		}
		return
	}

	if hasUnfriendlyAnnotation {
		err = &ExtractKeySpaceIDError{
			Kind:    ExtractKeySpaceIDReplicationUnfriendlyError,
			Message: fmt.Sprintf("Statement: %v", sql),
		}
		keyspaceID = nil
		return
	}

	// No annotations.
	keyspaceID = nil
	err = &ExtractKeySpaceIDError{
		Kind:    ExtractKeySpaceIDParseError,
		Message: fmt.Sprintf("No annotation found in '%v'", sql),
	}
	return
}

// Extracts the string from source contained between the leftmost instance of
// 'leftDelim' and the next instance of 'rightDelim'. If there is no next instance
// of 'rightDelim', returns the string contained between the end of the leftmost instance
// of 'leftDelim' to the end of 'source'. If 'leftDelim' does not appear in 'source',
// sets 'found' to false and 'match' to the empty string, otherwise 'found' is set to true
// and 'match' is set to the extracted string.
func extractStringBetween(source string, leftDelim string, rightDelim string) (match string, found bool) {
	leftDelimStart := strings.Index(source, leftDelim)
	if leftDelimStart == -1 {
		found = false
		match = ""
		return
	}
	found = true
	matchStart := leftDelimStart + len(leftDelim)
	matchEnd := strings.Index(source[matchStart:], rightDelim)
	if matchEnd != -1 {
		match = source[matchStart : matchStart+matchEnd]
		return
	}
	match = source[matchStart:]
	return
}

// ExtractKeySpaceIDError is the error type returned
// from ExtractKeySpaceID
// Kind is a numeric code for the error (see constants below)
// and Message is an error message string.
type ExtractKeySpaceIDError struct {
	Kind    int
	Message string
}

// Possible values for ExtractKeySpaceIDError.Kind
const (
	ExtractKeySpaceIDParseError                 = iota
	ExtractKeySpaceIDReplicationUnfriendlyError = iota
)

func (err ExtractKeySpaceIDError) Error() string {
	switch err.Kind {
	case ExtractKeySpaceIDParseError:
		return fmt.Sprintf("Parse-Error. %v", err.Message)
	case ExtractKeySpaceIDReplicationUnfriendlyError:
		return fmt.Sprintf(
			"Statement is filtered-replication-unfriendly. %v", err.Message)
	default:
		log.Fatalf("Unknown error type: %v", err)
		return "" // Unreachable.
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google Inc.

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 memcache provides a client for the memcached cache server.
package memcache

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"
)

const testServer = "localhost:11211"

func setup(t *testing.T) bool {
	c, err := net.Dial("tcp", testServer)
	if err != nil {
		t.Logf("skipping test; no server running at %s", testServer)
		return false
	}
	c.Write([]byte("flush_all\r\n"))
	c.Close()
	return true
}

func TestLocalhost(t *testing.T) {
	if !setup(t) {
		return
	}
	testWithClient(t, New(testServer))
}

// Run the memcached binary as a child process and connect to its unix socket.
func TestUnixSocket(t *testing.T) {
	sock := fmt.Sprintf("/tmp/test-gomemcache-%d.sock", os.Getpid())
	cmd := exec.Command("memcached", "-s", sock)
	if err := cmd.Start(); err != nil {
		t.Logf("skipping test; couldn't find memcached")
		return
	}
	defer cmd.Wait()
	defer cmd.Process.Kill()

	// Wait a bit for the socket to appear.
	for i := 0; i < 10; i++ {
		if _, err := os.Stat(sock); err == nil {
			break
		}
		time.Sleep(time.Duration(25*i) * time.Millisecond)
	}

	testWithClient(t, New(sock))
}

func testWithClient(t *testing.T, c *Client) {
	checkErr := func(err error, format string, args ...interface{}) {
		if err != nil {
			t.Fatalf(format, args...)
		}
	}

	mustSet := func(it *Item) {
		if err := c.Set(it); err != nil {
			t.Fatalf("failed to Set %#v: %v", *it, err)
		}
	}

	// Set
	foo := &Item{Key: "foo", Value: []byte("fooval"), Flags: 123}
	err := c.Set(foo)
	checkErr(err, "first set(foo): %v", err)
	err = c.Set(foo)
	checkErr(err, "second set(foo): %v", err)

	// Get
	it, err := c.Get("foo")
	checkErr(err, "get(foo): %v", err)
	if it.Key != "foo" {
		t.Errorf("get(foo) Key = %q, want foo", it.Key)
	}
	if string(it.Value) != "fooval" {
		t.Errorf("get(foo) Value = %q, want fooval", string(it.Value))
	}
	if it.Flags != 123 {
		t.Errorf("get(foo) Flags = %v, want 123", it.Flags)
	}

	// Add
	bar := &Item{Key: "bar", Value: []byte("barval")}
	err = c.Add(bar)
	checkErr(err, "first add(foo): %v", err)
	if err := c.Add(bar); err != ErrNotStored {
		t.Fatalf("second add(foo) want ErrNotStored, got %v", err)
	}

	// GetMulti
	m, err := c.GetMulti([]string{"foo", "bar"})
	checkErr(err, "GetMulti: %v", err)
	if g, e := len(m), 2; g != e {
		t.Errorf("GetMulti: got len(map) = %d, want = %d", g, e)
	}
	if _, ok := m["foo"]; !ok {
		t.Fatalf("GetMulti: didn't get key 'foo'")
	}
	if _, ok := m["bar"]; !ok {
		t.Fatalf("GetMulti: didn't get key 'bar'")
	}
	if g, e := string(m["foo"].Value), "fooval"; g != e {
		t.Errorf("GetMulti: foo: got %q, want %q", g, e)
	}
	if g, e := string(m["bar"].Value), "barval"; g != e {
		t.Errorf("GetMulti: bar: got %q, want %q", g, e)
	}

	// Delete
	err = c.Delete("foo")
	checkErr(err, "Delete: %v", err)
	it, err = c.Get("foo")
	if err != ErrCacheMiss {
		t.Errorf("post-Delete want ErrCacheMiss, got %v", err)
	}

	// Incr/Decr
	mustSet(&Item{Key: "num", Value: []byte("42")})
	n, err := c.Increment("num", 8)
	checkErr(err, "Increment num + 8: %v", err)
	if n != 50 {
		t.Fatalf("Increment num + 8: want=50, got=%d", n)
	}
	n, err = c.Decrement("num", 49)
	checkErr(err, "Decrement: %v", err)
	if n != 1 {
		t.Fatalf("Decrement 49: want=1, got=%d", n)
	}
	err = c.Delete("num")
	checkErr(err, "delete num: %v", err)
	n, err = c.Increment("num", 1)
	if err != ErrCacheMiss {
		t.Fatalf("increment post-delete: want ErrCacheMiss, got %v", err)
	}
	mustSet(&Item{Key: "num", Value: []byte("not-numeric")})
	n, err = c.Increment("num", 1)
	if err == nil || !strings.Contains(err.Error(), "client error") {
		t.Fatalf("increment non-number: want client error, got %v", err)
	}

}
<commit_msg>Fix key name in debug message<commit_after>/*
Copyright 2011 Google Inc.

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 memcache provides a client for the memcached cache server.
package memcache

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"
)

const testServer = "localhost:11211"

func setup(t *testing.T) bool {
	c, err := net.Dial("tcp", testServer)
	if err != nil {
		t.Logf("skipping test; no server running at %s", testServer)
		return false
	}
	c.Write([]byte("flush_all\r\n"))
	c.Close()
	return true
}

func TestLocalhost(t *testing.T) {
	if !setup(t) {
		return
	}
	testWithClient(t, New(testServer))
}

// Run the memcached binary as a child process and connect to its unix socket.
func TestUnixSocket(t *testing.T) {
	sock := fmt.Sprintf("/tmp/test-gomemcache-%d.sock", os.Getpid())
	cmd := exec.Command("memcached", "-s", sock)
	if err := cmd.Start(); err != nil {
		t.Logf("skipping test; couldn't find memcached")
		return
	}
	defer cmd.Wait()
	defer cmd.Process.Kill()

	// Wait a bit for the socket to appear.
	for i := 0; i < 10; i++ {
		if _, err := os.Stat(sock); err == nil {
			break
		}
		time.Sleep(time.Duration(25*i) * time.Millisecond)
	}

	testWithClient(t, New(sock))
}

func testWithClient(t *testing.T, c *Client) {
	checkErr := func(err error, format string, args ...interface{}) {
		if err != nil {
			t.Fatalf(format, args...)
		}
	}

	mustSet := func(it *Item) {
		if err := c.Set(it); err != nil {
			t.Fatalf("failed to Set %#v: %v", *it, err)
		}
	}

	// Set
	foo := &Item{Key: "foo", Value: []byte("fooval"), Flags: 123}
	err := c.Set(foo)
	checkErr(err, "first set(foo): %v", err)
	err = c.Set(foo)
	checkErr(err, "second set(foo): %v", err)

	// Get
	it, err := c.Get("foo")
	checkErr(err, "get(foo): %v", err)
	if it.Key != "foo" {
		t.Errorf("get(foo) Key = %q, want foo", it.Key)
	}
	if string(it.Value) != "fooval" {
		t.Errorf("get(foo) Value = %q, want fooval", string(it.Value))
	}
	if it.Flags != 123 {
		t.Errorf("get(foo) Flags = %v, want 123", it.Flags)
	}

	// Add
	bar := &Item{Key: "bar", Value: []byte("barval")}
	err = c.Add(bar)
	checkErr(err, "first add(bar): %v", err)
	if err := c.Add(bar); err != ErrNotStored {
		t.Fatalf("second add(bar) want ErrNotStored, got %v", err)
	}

	// GetMulti
	m, err := c.GetMulti([]string{"foo", "bar"})
	checkErr(err, "GetMulti: %v", err)
	if g, e := len(m), 2; g != e {
		t.Errorf("GetMulti: got len(map) = %d, want = %d", g, e)
	}
	if _, ok := m["foo"]; !ok {
		t.Fatalf("GetMulti: didn't get key 'foo'")
	}
	if _, ok := m["bar"]; !ok {
		t.Fatalf("GetMulti: didn't get key 'bar'")
	}
	if g, e := string(m["foo"].Value), "fooval"; g != e {
		t.Errorf("GetMulti: foo: got %q, want %q", g, e)
	}
	if g, e := string(m["bar"].Value), "barval"; g != e {
		t.Errorf("GetMulti: bar: got %q, want %q", g, e)
	}

	// Delete
	err = c.Delete("foo")
	checkErr(err, "Delete: %v", err)
	it, err = c.Get("foo")
	if err != ErrCacheMiss {
		t.Errorf("post-Delete want ErrCacheMiss, got %v", err)
	}

	// Incr/Decr
	mustSet(&Item{Key: "num", Value: []byte("42")})
	n, err := c.Increment("num", 8)
	checkErr(err, "Increment num + 8: %v", err)
	if n != 50 {
		t.Fatalf("Increment num + 8: want=50, got=%d", n)
	}
	n, err = c.Decrement("num", 49)
	checkErr(err, "Decrement: %v", err)
	if n != 1 {
		t.Fatalf("Decrement 49: want=1, got=%d", n)
	}
	err = c.Delete("num")
	checkErr(err, "delete num: %v", err)
	n, err = c.Increment("num", 1)
	if err != ErrCacheMiss {
		t.Fatalf("increment post-delete: want ErrCacheMiss, got %v", err)
	}
	mustSet(&Item{Key: "num", Value: []byte("not-numeric")})
	n, err = c.Increment("num", 1)
	if err == nil || !strings.Contains(err.Error(), "client error") {
		t.Fatalf("increment non-number: want client error, got %v", err)
	}

}
<|endoftext|>