<commit_before>package main

import (
	"fmt"
	"io"
	"strconv"
)

var (
	// stack is used to store variable names and values.
	stack map[string]Value
)

func init() {
	stack = make(map[string]Value)
}

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

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

// scan returns the next token from the underlying scanner.
// if a token has been unscanned then read that instead.
func (p *Parser) scan() (t Token, lit string) {

	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.t, p.buf.lit
	}

	t, lit = p.s.Scan()

	p.buf.t, p.buf.lit = t, lit
	return
}

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

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

// Expression is the interface for an expression.
type Expression interface {
	String() string
	Evaluate() Value
}

// Statement represents a code statement a = 2.
type Statement struct {
	Left  Value
	Right Value
}

// String retuns the statement as a string.
func (s Statement) String() string {
	return fmt.Sprintf("%v%v", s.Left, s.Right)
}

// Evaluate evaluates the given statement.
func (s Statement) Evaluate() Value {
	// todo(santiaago): need to think what this should really do..
	return s.Left
}

// Num represents a number statement.
type Num struct {
	n string
}

// String returns the string of the number
func (n Num) String() string {
	return fmt.Sprintf("%v", n.n)
}

// Evaluate returns the number value
func (n Num) Evaluate() Value {
	return n
}

// Value is an interface to handle different types.
type Value interface {
	String() string
	Evaluate() Value
}

// Int is a type to handle integers
type Int int64

// String returns the string representation of an integer.
func (i Int) String() string {
	return fmt.Sprintf("%d", i)
}

// Evaluate returns the value of the given integer.
func (i Int) Evaluate() Value {
	return i
}

func tryIntString(s string) (Value, error) {
	i, err := strconv.ParseInt(s, 10, 64)
	return Int(i), err
}

// Variable represents a variable.
type Variable struct {
	name string
}

// String returns the string representation of a variable.
func (v Variable) String() string {
	return v.name
}

// Evaluate returns the value holded by the variable v
func (v Variable) Evaluate() Value {
	if val, ok := stack[v.name]; ok {
		return val
	}
	return nil
}

// Binary represents a binary statement
// example: 12 + 3
type Binary struct {
	Left     Value
	Right    Value
	Operator string
}

// String returns the string of the number
func (b Binary) String() string {
	return fmt.Sprintf("%v %v %v", b.Left, b.Operator, b.Right)
}

// Evaluate returns the number value
func (b Binary) Evaluate() Value {
	if b.Operator == "+" {
		return add(b.Left, b.Right)
	} else if b.Operator == "-" {
		return minus(b.Left, b.Right)
	} else if b.Operator == "*" {
		return times(b.Left, b.Right)
	}
	return nil
}

func add(a, b Value) Value {
	// todo(santiaago): will have to check types at some point
	return Int(a.(Int) + b.(Int))
}

func minus(a, b Value) Value {
	// todo(santiaago): will have to check types at some point
	return Int(a.(Int) - b.(Int))
}

func times(a, b Value) Value {
	// todo(santiaago): will have to check types at some point
	return Int(a.(Int) * b.(Int))
}

// ValueParse parse the string in the proper value
// todo(santiaago): will have to try different types here...
func ValueParse(s string) Value {
	v, err := tryIntString(s)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	return v
}

// Parse parse a assign statement a = b
func (p *Parser) Parse() (*Expression, error) {

	var left, right, operator string
	tok, lit := p.scanIgnoreWhitespace()
	lastTok := tok
	if tok == Identifier {
		left = lit
	} else if tok == Number {
		left = lit
	} else {
		return nil, fmt.Errorf("found %q, expected left", lit)
	}

	tok, lit = p.scanIgnoreWhitespace()
	if tok == EOF {
		if lastTok == Number {
			e := Expression(Num{left})
			return &e, nil
		} else if lastTok == Identifier {
			// todo(santiaago): should check for variable existance?
			expr := Expression(Variable{name: left})
			if expr.Evaluate() == nil {
				return nil, fmt.Errorf("ERROR")
			}
			return &expr, nil
		} else {
			fmt.Println("jump", lastTok)
		}
	}

	isAssign := tok == Assign
	if tok != Assign && tok != Operator {
		return nil, fmt.Errorf("found %q, expected '=' with tok: %v, expected %v", lit, tok, Assign)
	}

	if tok == Operator {
		operator = lit
	}

	tok, lit = p.scanIgnoreWhitespace()
	if tok == Number {
		if !isAssign {
			right = lit
		} else {
			stack[left] = ValueParse(lit)
			expr := Expression(Variable{name: left})
			return &expr, nil
		}
	} else if tok == Identifier {
		if !isAssign {
			right = lit
		}
	} else {
		return nil, fmt.Errorf("found %q, expected identifier name", lit)
	}

	var expr Expression
	if isAssign {
		l := ValueParse(left)
		r := ValueParse(right)
		expr = Expression(Statement{Left: l, Right: r})
	} else {
		var l, r Value
		if val, ok := stack[left]; ok {
			l = val
		} else {
			l = ValueParse(left)
		}
		if val, ok := stack[right]; ok {
			r = val
		} else {
			r = ValueParse(right)
		}
		expr = Expression(Binary{Left: l, Right: r, Operator: operator})
	}
	return &expr, nil
}
<commit_msg>And do multiple arithmetic operations at a time<commit_after>package main

import (
	"fmt"
	"io"
	"strconv"
)

var (
	// stack is used to store variable names and values.
	stack map[string]Value
)

func init() {
	stack = make(map[string]Value)
}

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

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

// scan returns the next token from the underlying scanner.
// if a token has been unscanned then read that instead.
func (p *Parser) scan() (t Token, lit string) {

	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.t, p.buf.lit
	}

	t, lit = p.s.Scan()

	p.buf.t, p.buf.lit = t, lit
	return
}

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

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

// Expression is the interface for an expression.
type Expression interface {
	String() string
	Evaluate() Value
}

// Statement represents a code statement a = 2.
type Statement struct {
	Left  Value
	Right Value
}

// String retuns the statement as a string.
func (s Statement) String() string {
	return fmt.Sprintf("%v%v", s.Left, s.Right)
}

// Evaluate evaluates the given statement.
func (s Statement) Evaluate() Value {
	// todo(santiaago): need to think what this should really do..
	return s.Left
}

// Num represents a number statement.
type Num struct {
	n string
}

// String returns the string of the number
func (n Num) String() string {
	return fmt.Sprintf("%v", n.n)
}

// Evaluate returns the number value
func (n Num) Evaluate() Value {
	return n
}

// Value is an interface to handle different types.
type Value interface {
	String() string
	Evaluate() Value
}

// Int is a type to handle integers
type Int int64

// String returns the string representation of an integer.
func (i Int) String() string {
	return fmt.Sprintf("%d", i)
}

// Evaluate returns the value of the given integer.
func (i Int) Evaluate() Value {
	return i
}

func tryIntString(s string) (Value, error) {
	i, err := strconv.ParseInt(s, 10, 64)
	return Int(i), err
}

// Variable represents a variable.
type Variable struct {
	name string
}

// String returns the string representation of a variable.
func (v Variable) String() string {
	return v.name
}

// Evaluate returns the value holded by the variable v
func (v Variable) Evaluate() Value {
	if val, ok := stack[v.name]; ok {
		return val
	}
	return nil
}

// Binary represents a binary statement
// example: 12 + 3
type Binary struct {
	Left     Value
	Right    Value
	Operator string
}

// String returns the string of the number
func (b Binary) String() string {
	return fmt.Sprintf("%v %v %v", b.Left, b.Operator, b.Right)
}

// Evaluate returns the number value
func (b Binary) Evaluate() Value {
	if b.Operator == "+" {
		return add(b.Left, b.Right)
	} else if b.Operator == "-" {
		return minus(b.Left, b.Right)
	} else if b.Operator == "*" {
		return times(b.Left, b.Right)
	}
	return nil
}

func add(a, b Value) Value {
	// todo(santiaago): will have to check types at some point
	return Int(a.(Int) + b.(Int))
}

func minus(a, b Value) Value {
	// todo(santiaago): will have to check types at some point
	return Int(a.(Int) - b.(Int))
}

func times(a, b Value) Value {
	// todo(santiaago): will have to check types at some point
	return Int(a.(Int) * b.(Int))
}

// ValueParse parse the string in the proper value
// todo(santiaago): will have to try different types here...
func ValueParse(s string) Value {
	v, err := tryIntString(s)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	return v
}

// Parse parse a assign statement a = b
func (p *Parser) Parse() (*Expression, error) {

	// First token can be an identifier or number (for now)
	var left, right, operator string
	tok, lit := p.scanIgnoreWhitespace()
	lastTok := tok
	if tok == Identifier {
		left = lit
	} else if tok == Number {
		left = lit
	} else {
		return nil, fmt.Errorf("found %q, expected left", lit)
	}

	// Next it could be EOF, an operator or an assignment (for now)
	tok, lit = p.scanIgnoreWhitespace()
	if tok == EOF {
		if lastTok == Number {
			e := Expression(Num{left})
			return &e, nil
		} else if lastTok == Identifier {
			expr := Expression(Variable{name: left})
			if expr.Evaluate() == nil {
				return nil, fmt.Errorf("ERROR")
			}
			return &expr, nil
		} else {
			fmt.Println("jump", lastTok)
		}
	}

	isOperator := tok == Operator
	isAssign := tok == Assign
	if !isAssign && !isOperator {
		return nil, fmt.Errorf("found %q, expected '=' with tok: %v, expected %v", lit, tok, Assign)
	}

	if isOperator {
		operator = lit
	}

	// Next: Take care of assign case.
	if isAssign {
		tok, lit = p.scanIgnoreWhitespace()
		if tok == Number {
			stack[left] = ValueParse(lit)
			expr := Expression(Variable{name: left})
			return &expr, nil
		}
		right = lit
		return buildExpression(isAssign, left, right, operator)
	}

	// Next: Take care of operator case.
	// We should loop over all our operators.
	var terms []string
	var operators []string

	// Initialize arrays with the first term and operator seen.
	terms = append(terms, left)
	operators = append(operators, operator)

	for {
		// Read a field.
		tok, lit = p.scanIgnoreWhitespace()
		if tok != Identifier && tok != Number {
			return nil, fmt.Errorf("found %q, expected number or identifier", lit)
		}
		terms = append(terms, lit)
		// Read operator
		tok, lit = p.scanIgnoreWhitespace()
		// If the next token is not an operator then break the loop
		if tok != Operator {
			p.unscan()
			break
		}
		operators = append(operators, lit)
	}

	// At this point we have terms and, operators.
	// We need now to process all of this.
	return buildOperatorExpression(terms, operators)
}

func buildExpression(isAssign bool, left, right, operator string) (*Expression, error) {
	var expr Expression
	if isAssign {
		l := ValueParse(left)
		r := ValueParse(right)
		expr = Expression(Statement{Left: l, Right: r})
	} else {
		var l, r Value
		if val, ok := stack[left]; ok {
			l = val
		} else {
			l = ValueParse(left)
		}
		if val, ok := stack[right]; ok {
			r = val
		} else {
			r = ValueParse(right)
		}
		expr = Expression(Binary{Left: l, Right: r, Operator: operator})
	}
	return &expr, nil
}

// At this point we have the following
// left, operator, terms, operators
// we need now to process all of this.
func buildOperatorExpression(terms, operators []string) (*Expression, error) {

	if len(terms)-1 != len(operators) {
		return nil, fmt.Errorf("ERROR terms and operators size mismatch")
	}

	var cumulExpr Expression

	first := terms[0]
	if _, ok := stack[first]; ok {
		cumulExpr = Expression(Variable{name: first})
	} else {
		cumulExpr = Expression(ValueParse(first))
	}

	for i := 0; i < len(operators); i++ {

		right := terms[i+1]
		op := operators[i]

		var r Value
		if val, ok := stack[right]; ok {
			r = val
		} else {
			r = ValueParse(right)
		}
		cumulExpr = Expression(Binary{Left: cumulExpr.Evaluate(), Right: r, Operator: op})
	}
	return &cumulExpr, nil
}
<|endoftext|>


<commit_before>package solr

// ResultParser is interface for parsing result from response
type ResultParser interface {
	Parse(response *SelectResponse) (*SolrResult, error)
}

type StandardResultParser struct {
}

func (parser *StandardResultParser) Parse(response *SelectResponse) (*SolrResult, error) {
	sr := &SolrResult{}
	sr.results = new(Collection)
	sr.status = response.status

	if response.status == 0 {
		parser.ParseResponse(response, sr)
		parser.ParseFacetCounts(response, sr)
		parser.ParseLighlighting(response, sr)
	}

	return sr, nil
}

func (parser *StandardResultParser) ParseResponse(response *SelectResponse, sr *SolrResult) {
	if response, ok := response.response["response"].(map[string]interface{}); ok {
		sr.results.numFound = int(response["numFound"].(float64))
		sr.results.start = int(response["start"].(float64))
		if docs, ok := response["docs"].([]interface{}); ok {
			for _, v := range docs {
				sr.results.docs = append(sr.results.docs, Document(v.(map[string]interface{})))
			}
		}
	} else {
		panic(`Standard parser can only parse solr response with response object,
					ie response.response and response.response.docs.
					Please use other parser or implement your own parser`)
	}
}

func (parser *StandardResultParser) ParseFacetCounts(response *SelectResponse, sr *SolrResult) {
	if facetCounts, ok := response.response["facet_counts"]; ok {
		sr.facet_counts = facetCounts.(map[string]interface{})
	}
}

func (parser *StandardResultParser) ParseLighlighting(response *SelectResponse, sr *SolrResult) {
	if highlighting, ok := response.response["highlighting"]; ok {
		sr.highlighting = highlighting.(map[string]interface{})
	}
}
<commit_msg>#2 Fix misspel of highlighting<commit_after>package solr

// ResultParser is interface for parsing result from response
type ResultParser interface {
	Parse(response *SelectResponse) (*SolrResult, error)
}

type StandardResultParser struct {
}

func (parser *StandardResultParser) Parse(response *SelectResponse) (*SolrResult, error) {
	sr := &SolrResult{}
	sr.results = new(Collection)
	sr.status = response.status

	if response.status == 0 {
		parser.ParseResponse(response, sr)
		parser.ParseFacetCounts(response, sr)
		parser.ParseHighlighting(response, sr)
	}

	return sr, nil
}

func (parser *StandardResultParser) ParseResponse(response *SelectResponse, sr *SolrResult) {
	if response, ok := response.response["response"].(map[string]interface{}); ok {
		sr.results.numFound = int(response["numFound"].(float64))
		sr.results.start = int(response["start"].(float64))
		if docs, ok := response["docs"].([]interface{}); ok {
			for _, v := range docs {
				sr.results.docs = append(sr.results.docs, Document(v.(map[string]interface{})))
			}
		}
	} else {
		panic(`Standard parser can only parse solr response with response object,
					ie response.response and response.response.docs.
					Please use other parser or implement your own parser`)
	}
}

func (parser *StandardResultParser) ParseFacetCounts(response *SelectResponse, sr *SolrResult) {
	if facetCounts, ok := response.response["facet_counts"]; ok {
		sr.facet_counts = facetCounts.(map[string]interface{})
	}
}

func (parser *StandardResultParser) ParseHighlighting(response *SelectResponse, sr *SolrResult) {
	if highlighting, ok := response.response["highlighting"]; ok {
		sr.highlighting = highlighting.(map[string]interface{})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"unicode/utf8"
)

type Node struct {
	tag string
	attrs map[string]string
	text string
}

type Parser struct {

	debug bool

	Scanner Scanner
	output string

	// node heirarchy
	stack []*Node

	// states as the Parser object figures out how to output the tokens

	// new line has started
	isNewLine bool

	isIndent bool
	isDedent bool
	dedentCount int

	// flag indicates we are still checking for attribute assignments
	isAttr bool

	// attrName and attrValue are buffers to determine if TokWord for a tag are
	// potentially for an attribute, or if they are just normal text
	attrName string
	attrValue string
	attrAssigned bool
	// dumb appended string while attribute assignment is still being determined
	// this is used if it turns out tokens are NOT part of an attribute assignment
	// and the string (with whitespace if any) gets appended to the node text
	attrString string

	node *Node
}

func (p *Parser) Output() string {
	var tokens []rune
	var text string

	p.stack = make([]*Node, 0)
	p.output = ""
	p.isNewLine = true
	p.isIndent = false
	p.isDedent = false
	p.isAttr = false
	p.attrName = ""
	p.attrValue = ""
	p.attrAssigned = false
	p.attrString = ""
	p.dedentCount = 0
	p.node = p.newNode()

	for {
		tokens = p.Scanner.Scan()
		for _, token := range tokens {
			text = p.Scanner.TokenText()
			if p.debug {
				fmt.Printf("[debug] token: [%s]\n", TokenString(token))
				fmt.Printf("[debug] text:  [%s]\n", text)
			}
			p.processToken(token, text)
			if p.debug {
				fmt.Printf("[debug] node:  [%s]\n\n", p.node)
			}
		}
		if tokens[0] == TokEOF {
			break
		}
	}
	return p.output
}

func (p *Parser) closeLastNode() {
	if len(p.stack) > 0 {
		oldNode := p.stack[len(p.stack) - 1]
		p.stack = p.stack[0: len(p.stack) - 1]
		if r, l := utf8.DecodeLastRuneInString(p.output); r == ' ' {
			p.output = p.output[0: len(p.output) - l]
		}
		p.output += "</" + oldNode.tag + ">"
	}
}

func (p *Parser) lastNode() *Node {
	return p.stack[len(p.stack) - 1]
}

func (p *Parser) outputNode(node *Node) {
	p.output += "<" + node.tag
	if len(node.attrs) > 0 {
		for k, v := range node.attrs {
			p.output += fmt.Sprintf(" %s='%s'", k, v)
		}
	}
	p.output += ">"
	p.output += node.text
}

func (p *Parser) processToken(tok rune, text string) {
	switch tok {
	case TokWord, TokComma:
		if p.node.tag == "" {
			p.node.tag = text
			// found the tag, now check for attributes
			p.isAttr = true
		} else if p.isAttr {
			if p.attrAssigned {
				if _, found := p.node.attrs[p.attrName]; found {
					p.node.attrs[p.attrName] += " "
					p.node.attrs[p.attrName] += text
				} else {
					p.node.attrs[p.attrName] = text
				}

				// reset to look for a new attribute assignment
				p.attrName = ""
				p.attrValue = ""
				p.attrAssigned = false
			} else if p.attrName == "" {
				p.attrString += text
				p.attrName = text
			} else {
				p.node.text += p.attrString
				p.node.text += text
				p.isAttr = false
				p.attrName = ""
				p.attrValue = ""
				p.attrAssigned = false
			}
		} else {
			p.node.text += text
		}
	case TokAssign:
		if p.isAttr {
			if p.attrName != "" && p.attrValue == "" {
				p.attrAssigned = true
				p.attrString += text
			}
			if p.attrName == "" || p.attrValue != "" {
				p.attrAssigned = false
				p.isAttr = false
				p.node.text += p.attrString
			}
		} else {
			p.node.text += text
		}
	case TokWhitespace:
		if p.isAttr && p.attrString != "" {
			p.attrString += text

		// skip initial whitespace for text
		} else if p.node.text != "" {
			p.node.text += text
		}
	case TokIndent:
		p.isIndent = true
		p.isDedent = false
	case TokDedent:
		p.isIndent = false
		p.isDedent = true
		p.dedentCount += 1
	case TokNewLine:
		// up to this point a node has been constructed but not appended to the
		// output
		if p.isDedent {
			// for dedents, pop off the stack and close the node
			for p.dedentCount > 0 {
				p.closeLastNode()
				p.dedentCount -= 1
			}
		}
		if p.isDedent || !p.isIndent {
			// closing one more time because this new node replaces the top node in the
			// stack for dedents or siblings (non indent)
			p.closeLastNode()
		}
		p.outputNode(p.node)
		p.stack = append(p.stack, p.node)

		// reset
		p.node = p.newNode()
		p.isIndent = false
		p.isDedent = false
		p.isAttr = false
		p.attrName = ""
		p.attrValue = ""
		p.attrString = ""
	case TokEOF:
		// since p.node is never on the stack until TokNewLine we push to the stack
		// in this scenario
		if p.node.tag != "" {
			p.stack = append(p.stack, p.node)
			p.outputNode(p.node)
		}
		// close the remaining nodes in the stack
		for len(p.stack) > 0 {
			p.closeLastNode()
		}
	default:
		if !p.isAttr {
			p.node.text += text
		}
	}
}

func (p *Parser) newNode() *Node {
	n := new(Node)
	n.attrs = make(map[string]string)
	return n
}
<commit_msg>proper handling of newlines with no nodes<commit_after>package main

import (
	"fmt"
	"unicode/utf8"
)

type Node struct {
	tag string
	attrs map[string]string
	text string
}

type Parser struct {

	debug bool

	Scanner Scanner
	output string

	// node heirarchy
	stack []*Node

	// states as the Parser object figures out how to output the tokens

	// new line has started
	isNewLine bool

	isIndent bool
	isDedent bool
	dedentCount int

	// flag indicates we are still checking for attribute assignments
	isAttr bool

	// attrName and attrValue are buffers to determine if TokWord for a tag are
	// potentially for an attribute, or if they are just normal text
	attrName string
	attrValue string
	attrAssigned bool
	// dumb appended string while attribute assignment is still being determined
	// this is used if it turns out tokens are NOT part of an attribute assignment
	// and the string (with whitespace if any) gets appended to the node text
	attrString string

	node *Node
}

func (p *Parser) Output() string {
	var tokens []rune
	var text string

	p.stack = make([]*Node, 0)
	p.output = ""
	p.isNewLine = true
	p.isIndent = false
	p.isDedent = false
	p.isAttr = false
	p.attrName = ""
	p.attrValue = ""
	p.attrAssigned = false
	p.attrString = ""
	p.dedentCount = 0
	p.node = p.newNode()

	for {
		tokens = p.Scanner.Scan()
		for _, token := range tokens {
			text = p.Scanner.TokenText()
			if p.debug {
				fmt.Printf("[debug] token: [%s]\n", TokenString(token))
				fmt.Printf("[debug] text:  [%s]\n", text)
			}
			p.processToken(token, text)
			if p.debug {
				fmt.Printf("[debug] node:  [%s]\n\n", p.node)
			}
		}
		if tokens[0] == TokEOF {
			break
		}
	}
	return p.output
}

func (p *Parser) closeLastNode() {
	if len(p.stack) > 0 {
		oldNode := p.stack[len(p.stack) - 1]
		p.stack = p.stack[0: len(p.stack) - 1]
		if r, l := utf8.DecodeLastRuneInString(p.output); r == ' ' {
			p.output = p.output[0: len(p.output) - l]
		}
		p.output += "</" + oldNode.tag + ">"
	}
}

func (p *Parser) lastNode() *Node {
	return p.stack[len(p.stack) - 1]
}

func (p *Parser) outputNode(node *Node) {
	if node.tag != "" {
		p.output += "<" + node.tag
		if len(node.attrs) > 0 {
			for k, v := range node.attrs {
				p.output += fmt.Sprintf(" %s='%s'", k, v)
			}
		}
		p.output += ">"
		p.output += node.text
	}
}
func (p *Parser) pushNode(node *Node) {
	if p.node.tag != "" {
		p.stack = append(p.stack, p.node)
	}
}

func (p *Parser) processToken(tok rune, text string) {
	switch tok {
	case TokWord, TokComma:
		if p.node.tag == "" {
			p.node.tag = text
			// found the tag, now check for attributes
			p.isAttr = true
		} else if p.isAttr {
			if p.attrAssigned {
				if _, found := p.node.attrs[p.attrName]; found {
					p.node.attrs[p.attrName] += " "
					p.node.attrs[p.attrName] += text
				} else {
					p.node.attrs[p.attrName] = text
				}

				// reset to look for a new attribute assignment
				p.attrName = ""
				p.attrValue = ""
				p.attrAssigned = false
			} else if p.attrName == "" {
				p.attrString += text
				p.attrName = text
			} else {
				p.node.text += p.attrString
				p.node.text += text
				p.isAttr = false
				p.attrName = ""
				p.attrValue = ""
				p.attrAssigned = false
			}
		} else {
			p.node.text += text
		}
	case TokAssign:
		if p.isAttr {
			if p.attrName != "" && p.attrValue == "" {
				p.attrAssigned = true
				p.attrString += text
			}
			if p.attrName == "" || p.attrValue != "" {
				p.attrAssigned = false
				p.isAttr = false
				p.node.text += p.attrString
			}
		} else {
			p.node.text += text
		}
	case TokWhitespace:
		if p.isAttr && p.attrString != "" {
			p.attrString += text

		// skip initial whitespace for text
		} else if p.node.text != "" {
			p.node.text += text
		}
	case TokIndent:
		p.isIndent = true
		p.isDedent = false
	case TokDedent:
		p.isIndent = false
		p.isDedent = true
		p.dedentCount += 1
	case TokNewLine:
		// up to this point a node has been constructed but not appended to the
		// output
		if p.isDedent {
			// for dedents, pop off the stack and close the node
			for p.dedentCount > 0 {
				p.closeLastNode()
				p.dedentCount -= 1
			}
		}
		if p.isDedent || !p.isIndent {
			// closing one more time because this new node replaces the top node in the
			// stack for dedents or siblings (non indent)
			p.closeLastNode()
		}
		p.outputNode(p.node)
		p.pushNode(p.node)

		// reset
		p.node = p.newNode()
		p.isIndent = false
		p.isDedent = false
		p.isAttr = false
		p.attrName = ""
		p.attrValue = ""
		p.attrString = ""
	case TokEOF:
		// since p.node is never on the stack until TokNewLine we push to the stack
		// in this scenario
		p.pushNode(p.node)
		p.outputNode(p.node)
		// close the remaining nodes in the stack
		for len(p.stack) > 0 {
			p.closeLastNode()
		}
	default:
		if !p.isAttr {
			p.node.text += text
		}
	}
}

func (p *Parser) newNode() *Node {
	n := new(Node)
	n.attrs = make(map[string]string)
	return n
}
<|endoftext|>


<commit_before>// Copyright 2015 Unknwon
//
// 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 ini

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"unicode"
)

var pythonMultiline = regexp.MustCompile("^(\\s+)([^\n]+)")

type tokenType int

const (
	_TOKEN_INVALID tokenType = iota
	_TOKEN_COMMENT
	_TOKEN_SECTION
	_TOKEN_KEY
)

type parser struct {
	buf     *bufio.Reader
	isEOF   bool
	count   int
	comment *bytes.Buffer
}

func newParser(r io.Reader) *parser {
	return &parser{
		buf:     bufio.NewReader(r),
		count:   1,
		comment: &bytes.Buffer{},
	}
}

// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format.
// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
func (p *parser) BOM() error {
	mask, err := p.buf.Peek(2)
	if err != nil && err != io.EOF {
		return err
	} else if len(mask) < 2 {
		return nil
	}

	switch {
	case mask[0] == 254 && mask[1] == 255:
		fallthrough
	case mask[0] == 255 && mask[1] == 254:
		p.buf.Read(mask)
	case mask[0] == 239 && mask[1] == 187:
		mask, err := p.buf.Peek(3)
		if err != nil && err != io.EOF {
			return err
		} else if len(mask) < 3 {
			return nil
		}
		if mask[2] == 191 {
			p.buf.Read(mask)
		}
	}
	return nil
}

func (p *parser) readUntil(delim byte) ([]byte, error) {
	data, err := p.buf.ReadBytes(delim)
	if err != nil {
		if err == io.EOF {
			p.isEOF = true
		} else {
			return nil, err
		}
	}
	return data, nil
}

func cleanComment(in []byte) ([]byte, bool) {
	i := bytes.IndexAny(in, "#;")
	if i == -1 {
		return nil, false
	}
	return in[i:], true
}

func readKeyName(delimiters string, in []byte) (string, int, error) {
	line := string(in)

	// Check if key name surrounded by quotes.
	var keyQuote string
	if line[0] == '"' {
		if len(line) > 6 && string(line[0:3]) == `"""` {
			keyQuote = `"""`
		} else {
			keyQuote = `"`
		}
	} else if line[0] == '`' {
		keyQuote = "`"
	}

	// Get out key name
	endIdx := -1
	if len(keyQuote) > 0 {
		startIdx := len(keyQuote)
		// FIXME: fail case -> """"""name"""=value
		pos := strings.Index(line[startIdx:], keyQuote)
		if pos == -1 {
			return "", -1, fmt.Errorf("missing closing key quote: %s", line)
		}
		pos += startIdx

		// Find key-value delimiter
		i := strings.IndexAny(line[pos+startIdx:], delimiters)
		if i < 0 {
			return "", -1, ErrDelimiterNotFound{line}
		}
		endIdx = pos + i
		return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil
	}

	endIdx = strings.IndexAny(line, delimiters)
	if endIdx < 0 {
		return "", -1, ErrDelimiterNotFound{line}
	}
	return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil
}

func (p *parser) readMultilines(line, val, valQuote string) (string, error) {
	for {
		data, err := p.readUntil('\n')
		if err != nil {
			return "", err
		}
		next := string(data)

		pos := strings.LastIndex(next, valQuote)
		if pos > -1 {
			val += next[:pos]

			comment, has := cleanComment([]byte(next[pos:]))
			if has {
				p.comment.Write(bytes.TrimSpace(comment))
			}
			break
		}
		val += next
		if p.isEOF {
			return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next)
		}
	}
	return val, nil
}

func (p *parser) readContinuationLines(val string) (string, error) {
	for {
		data, err := p.readUntil('\n')
		if err != nil {
			return "", err
		}
		next := strings.TrimSpace(string(data))

		if len(next) == 0 {
			break
		}
		val += next
		if val[len(val)-1] != '\\' {
			break
		}
		val = val[:len(val)-1]
	}
	return val, nil
}

// hasSurroundedQuote check if and only if the first and last characters
// are quotes \" or \'.
// It returns false if any other parts also contain same kind of quotes.
func hasSurroundedQuote(in string, quote byte) bool {
	return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote &&
		strings.IndexByte(in[1:], quote) == len(in)-2
}

func (p *parser) readValue(in []byte,
	parserBufferSize int,
	ignoreContinuation, ignoreInlineComment, unescapeValueDoubleQuotes, unescapeValueCommentSymbols, allowPythonMultilines, spaceBeforeInlineComment, preserveSurroundedQuote bool) (string, error) {

	line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
	if len(line) == 0 {
		return "", nil
	}

	var valQuote string
	if len(line) > 3 && string(line[0:3]) == `"""` {
		valQuote = `"""`
	} else if line[0] == '`' {
		valQuote = "`"
	} else if unescapeValueDoubleQuotes && line[0] == '"' {
		valQuote = `"`
	}

	if len(valQuote) > 0 {
		startIdx := len(valQuote)
		pos := strings.LastIndex(line[startIdx:], valQuote)
		// Check for multi-line value
		if pos == -1 {
			return p.readMultilines(line, line[startIdx:], valQuote)
		}

		if unescapeValueDoubleQuotes && valQuote == `"` {
			return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil
		}
		return line[startIdx : pos+startIdx], nil
	}

	lastChar := line[len(line)-1]
	// Won't be able to reach here if value only contains whitespace
	line = strings.TrimSpace(line)
	trimmedLastChar := line[len(line)-1]

	// Check continuation lines when desired
	if !ignoreContinuation && trimmedLastChar == '\\' {
		return p.readContinuationLines(line[:len(line)-1])
	}

	// Check if ignore inline comment
	if !ignoreInlineComment {
		var i int
		if spaceBeforeInlineComment {
			i = strings.Index(line, " #")
			if i == -1 {
				i = strings.Index(line, " ;")
			}

		} else {
			i = strings.IndexAny(line, "#;")
		}

		if i > -1 {
			p.comment.WriteString(line[i:])
			line = strings.TrimSpace(line[:i])
		}

	}

	// Trim single and double quotes
	if (hasSurroundedQuote(line, '\'') ||
		hasSurroundedQuote(line, '"')) && !preserveSurroundedQuote {
		line = line[1 : len(line)-1]
	} else if len(valQuote) == 0 && unescapeValueCommentSymbols {
		if strings.Contains(line, `\;`) {
			line = strings.Replace(line, `\;`, ";", -1)
		}
		if strings.Contains(line, `\#`) {
			line = strings.Replace(line, `\#`, "#", -1)
		}
	} else if allowPythonMultilines && lastChar == '\n' {
		parserBufferPeekResult, _ := p.buf.Peek(parserBufferSize)
		peekBuffer := bytes.NewBuffer(parserBufferPeekResult)

		val := line

		for {
			peekData, peekErr := peekBuffer.ReadBytes('\n')
			if peekErr != nil {
				if peekErr == io.EOF {
					return val, nil
				}
				return "", peekErr
			}

			peekMatches := pythonMultiline.FindStringSubmatch(string(peekData))
			if len(peekMatches) != 3 {
				return val, nil
			}

			// NOTE: Return if not a python-ini multi-line value.
			currentIdentSize := len(peekMatches[1])
			if currentIdentSize <= 0 {
				return val, nil
			}

			// NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer.
			_, err := p.readUntil('\n')
			if err != nil {
				return "", err
			}

			val += fmt.Sprintf("\n%s", peekMatches[2])
		}
	}

	return line, nil
}

// parse parses data through an io.Reader.
func (f *File) parse(reader io.Reader) (err error) {
	p := newParser(reader)
	if err = p.BOM(); err != nil {
		return fmt.Errorf("BOM: %v", err)
	}

	// Ignore error because default section name is never empty string.
	name := DEFAULT_SECTION
	if f.options.Insensitive {
		name = strings.ToLower(DEFAULT_SECTION)
	}
	section, _ := f.NewSection(name)

	// This "last" is not strictly equivalent to "previous one" if current key is not the first nested key
	var isLastValueEmpty bool
	var lastRegularKey *Key

	var line []byte
	var inUnparseableSection bool

	// NOTE: Iterate and increase `currentPeekSize` until
	// the size of the parser buffer is found.
	// TODO(unknwon): When Golang 1.10 is the lowest version supported, replace with `parserBufferSize := p.buf.Size()`.
	parserBufferSize := 0
	// NOTE: Peek 1kb at a time.
	currentPeekSize := 1024

	if f.options.AllowPythonMultilineValues {
		for {
			peekBytes, _ := p.buf.Peek(currentPeekSize)
			peekBytesLength := len(peekBytes)

			if parserBufferSize >= peekBytesLength {
				break
			}

			currentPeekSize *= 2
			parserBufferSize = peekBytesLength
		}
	}

	for !p.isEOF {
		line, err = p.readUntil('\n')
		if err != nil {
			return err
		}

		if f.options.AllowNestedValues &&
			isLastValueEmpty && len(line) > 0 {
			if line[0] == ' ' || line[0] == '\t' {
				lastRegularKey.addNestedValue(string(bytes.TrimSpace(line)))
				continue
			}
		}

		line = bytes.TrimLeftFunc(line, unicode.IsSpace)
		if len(line) == 0 {
			continue
		}

		// Comments
		if line[0] == '#' || line[0] == ';' {
			// Note: we do not care ending line break,
			// it is needed for adding second line,
			// so just clean it once at the end when set to value.
			p.comment.Write(line)
			continue
		}

		// Section
		if line[0] == '[' {
			// Read to the next ']' (TODO: support quoted strings)
			closeIdx := bytes.LastIndexByte(line, ']')
			if closeIdx == -1 {
				return fmt.Errorf("unclosed section: %s", line)
			}

			name := string(line[1:closeIdx])
			section, err = f.NewSection(name)
			if err != nil {
				return err
			}

			comment, has := cleanComment(line[closeIdx+1:])
			if has {
				p.comment.Write(comment)
			}

			section.Comment = strings.TrimSpace(p.comment.String())

			// Reset aotu-counter and comments
			p.comment.Reset()
			p.count = 1

			inUnparseableSection = false
			for i := range f.options.UnparseableSections {
				if f.options.UnparseableSections[i] == name ||
					(f.options.Insensitive && strings.ToLower(f.options.UnparseableSections[i]) == strings.ToLower(name)) {
					inUnparseableSection = true
					continue
				}
			}
			continue
		}

		if inUnparseableSection {
			section.isRawSection = true
			section.rawBody += string(line)
			continue
		}

		kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line)
		if err != nil {
			// Treat as boolean key when desired, and whole line is key name.
			if IsErrDelimiterNotFound(err) {
				switch {
				case f.options.AllowBooleanKeys:
					kname, err := p.readValue(line,
						parserBufferSize,
						f.options.IgnoreContinuation,
						f.options.IgnoreInlineComment,
						f.options.UnescapeValueDoubleQuotes,
						f.options.UnescapeValueCommentSymbols,
						f.options.AllowPythonMultilineValues,
						f.options.SpaceBeforeInlineComment,
						f.options.PreserveSurroundedQuote)
					if err != nil {
						return err
					}
					key, err := section.NewBooleanKey(kname)
					if err != nil {
						return err
					}
					key.Comment = strings.TrimSpace(p.comment.String())
					p.comment.Reset()
					continue

				case f.options.SkipUnrecognizableLines:
					continue
				}
			}
			return err
		}

		// Auto increment.
		isAutoIncr := false
		if kname == "-" {
			isAutoIncr = true
			kname = "#" + strconv.Itoa(p.count)
			p.count++
		}

		value, err := p.readValue(line[offset:],
			parserBufferSize,
			f.options.IgnoreContinuation,
			f.options.IgnoreInlineComment,
			f.options.UnescapeValueDoubleQuotes,
			f.options.UnescapeValueCommentSymbols,
			f.options.AllowPythonMultilineValues,
			f.options.SpaceBeforeInlineComment,
			f.options.PreserveSurroundedQuote)
		if err != nil {
			return err
		}
		isLastValueEmpty = len(value) == 0

		key, err := section.NewKey(kname, value)
		if err != nil {
			return err
		}
		key.isAutoIncrement = isAutoIncr
		key.Comment = strings.TrimSpace(p.comment.String())
		p.comment.Reset()
		lastRegularKey = key
	}
	return nil
}
<commit_msg>parser: pass file options at parser creation<commit_after>// Copyright 2015 Unknwon
//
// 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 ini

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"regexp"
	"strconv"
	"strings"
	"unicode"
)

var pythonMultiline = regexp.MustCompile("^(\\s+)([^\n]+)")

type parserOptions struct {
	IgnoreContinuation          bool
	IgnoreInlineComment         bool
	AllowPythonMultilineValues  bool
	SpaceBeforeInlineComment    bool
	UnescapeValueDoubleQuotes   bool
	UnescapeValueCommentSymbols bool
	PreserveSurroundedQuote     bool
}

type parser struct {
	buf     *bufio.Reader
	options parserOptions

	isEOF   bool
	count   int
	comment *bytes.Buffer
}

func newParser(r io.Reader, opts parserOptions) *parser {
	return &parser{
		buf:     bufio.NewReader(r),
		options: opts,
		count:   1,
		comment: &bytes.Buffer{},
	}
}

// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format.
// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding
func (p *parser) BOM() error {
	mask, err := p.buf.Peek(2)
	if err != nil && err != io.EOF {
		return err
	} else if len(mask) < 2 {
		return nil
	}

	switch {
	case mask[0] == 254 && mask[1] == 255:
		fallthrough
	case mask[0] == 255 && mask[1] == 254:
		p.buf.Read(mask)
	case mask[0] == 239 && mask[1] == 187:
		mask, err := p.buf.Peek(3)
		if err != nil && err != io.EOF {
			return err
		} else if len(mask) < 3 {
			return nil
		}
		if mask[2] == 191 {
			p.buf.Read(mask)
		}
	}
	return nil
}

func (p *parser) readUntil(delim byte) ([]byte, error) {
	data, err := p.buf.ReadBytes(delim)
	if err != nil {
		if err == io.EOF {
			p.isEOF = true
		} else {
			return nil, err
		}
	}
	return data, nil
}

func cleanComment(in []byte) ([]byte, bool) {
	i := bytes.IndexAny(in, "#;")
	if i == -1 {
		return nil, false
	}
	return in[i:], true
}

func readKeyName(delimiters string, in []byte) (string, int, error) {
	line := string(in)

	// Check if key name surrounded by quotes.
	var keyQuote string
	if line[0] == '"' {
		if len(line) > 6 && string(line[0:3]) == `"""` {
			keyQuote = `"""`
		} else {
			keyQuote = `"`
		}
	} else if line[0] == '`' {
		keyQuote = "`"
	}

	// Get out key name
	endIdx := -1
	if len(keyQuote) > 0 {
		startIdx := len(keyQuote)
		// FIXME: fail case -> """"""name"""=value
		pos := strings.Index(line[startIdx:], keyQuote)
		if pos == -1 {
			return "", -1, fmt.Errorf("missing closing key quote: %s", line)
		}
		pos += startIdx

		// Find key-value delimiter
		i := strings.IndexAny(line[pos+startIdx:], delimiters)
		if i < 0 {
			return "", -1, ErrDelimiterNotFound{line}
		}
		endIdx = pos + i
		return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil
	}

	endIdx = strings.IndexAny(line, delimiters)
	if endIdx < 0 {
		return "", -1, ErrDelimiterNotFound{line}
	}
	return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil
}

func (p *parser) readMultilines(line, val, valQuote string) (string, error) {
	for {
		data, err := p.readUntil('\n')
		if err != nil {
			return "", err
		}
		next := string(data)

		pos := strings.LastIndex(next, valQuote)
		if pos > -1 {
			val += next[:pos]

			comment, has := cleanComment([]byte(next[pos:]))
			if has {
				p.comment.Write(bytes.TrimSpace(comment))
			}
			break
		}
		val += next
		if p.isEOF {
			return "", fmt.Errorf("missing closing key quote from '%s' to '%s'", line, next)
		}
	}
	return val, nil
}

func (p *parser) readContinuationLines(val string) (string, error) {
	for {
		data, err := p.readUntil('\n')
		if err != nil {
			return "", err
		}
		next := strings.TrimSpace(string(data))

		if len(next) == 0 {
			break
		}
		val += next
		if val[len(val)-1] != '\\' {
			break
		}
		val = val[:len(val)-1]
	}
	return val, nil
}

// hasSurroundedQuote check if and only if the first and last characters
// are quotes \" or \'.
// It returns false if any other parts also contain same kind of quotes.
func hasSurroundedQuote(in string, quote byte) bool {
	return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote &&
		strings.IndexByte(in[1:], quote) == len(in)-2
}

func (p *parser) readValue(in []byte, bufferSize int) (string, error) {

	line := strings.TrimLeftFunc(string(in), unicode.IsSpace)
	if len(line) == 0 {
		return "", nil
	}

	var valQuote string
	if len(line) > 3 && string(line[0:3]) == `"""` {
		valQuote = `"""`
	} else if line[0] == '`' {
		valQuote = "`"
	} else if p.options.UnescapeValueDoubleQuotes && line[0] == '"' {
		valQuote = `"`
	}

	if len(valQuote) > 0 {
		startIdx := len(valQuote)
		pos := strings.LastIndex(line[startIdx:], valQuote)
		// Check for multi-line value
		if pos == -1 {
			return p.readMultilines(line, line[startIdx:], valQuote)
		}

		if p.options.UnescapeValueDoubleQuotes && valQuote == `"` {
			return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil
		}
		return line[startIdx : pos+startIdx], nil
	}

	lastChar := line[len(line)-1]
	// Won't be able to reach here if value only contains whitespace
	line = strings.TrimSpace(line)
	trimmedLastChar := line[len(line)-1]

	// Check continuation lines when desired
	if !p.options.IgnoreContinuation && trimmedLastChar == '\\' {
		return p.readContinuationLines(line[:len(line)-1])
	}

	// Check if ignore inline comment
	if !p.options.IgnoreInlineComment {
		var i int
		if p.options.SpaceBeforeInlineComment {
			i = strings.Index(line, " #")
			if i == -1 {
				i = strings.Index(line, " ;")
			}

		} else {
			i = strings.IndexAny(line, "#;")
		}

		if i > -1 {
			p.comment.WriteString(line[i:])
			line = strings.TrimSpace(line[:i])
		}

	}

	// Trim single and double quotes
	if (hasSurroundedQuote(line, '\'') ||
		hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote {
		line = line[1 : len(line)-1]
	} else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols {
		if strings.Contains(line, `\;`) {
			line = strings.Replace(line, `\;`, ";", -1)
		}
		if strings.Contains(line, `\#`) {
			line = strings.Replace(line, `\#`, "#", -1)
		}
	} else if p.options.AllowPythonMultilineValues && lastChar == '\n' {
		parserBufferPeekResult, _ := p.buf.Peek(bufferSize)
		peekBuffer := bytes.NewBuffer(parserBufferPeekResult)

		val := line

		for {
			peekData, peekErr := peekBuffer.ReadBytes('\n')
			if peekErr != nil {
				if peekErr == io.EOF {
					return val, nil
				}
				return "", peekErr
			}

			peekMatches := pythonMultiline.FindStringSubmatch(string(peekData))
			if len(peekMatches) != 3 {
				return val, nil
			}

			// NOTE: Return if not a python-ini multi-line value.
			currentIdentSize := len(peekMatches[1])
			if currentIdentSize <= 0 {
				return val, nil
			}

			// NOTE: Just advance the parser reader (buffer) in-sync with the peek buffer.
			_, err := p.readUntil('\n')
			if err != nil {
				return "", err
			}

			val += fmt.Sprintf("\n%s", peekMatches[2])
		}
	}

	return line, nil
}

// parse parses data through an io.Reader.
func (f *File) parse(reader io.Reader) (err error) {
	p := newParser(reader, parserOptions{
		IgnoreContinuation:          f.options.IgnoreContinuation,
		IgnoreInlineComment:         f.options.IgnoreInlineComment,
		AllowPythonMultilineValues:  f.options.AllowPythonMultilineValues,
		SpaceBeforeInlineComment:    f.options.SpaceBeforeInlineComment,
		UnescapeValueDoubleQuotes:   f.options.UnescapeValueDoubleQuotes,
		UnescapeValueCommentSymbols: f.options.UnescapeValueCommentSymbols,
		PreserveSurroundedQuote:     f.options.PreserveSurroundedQuote,
	})
	if err = p.BOM(); err != nil {
		return fmt.Errorf("BOM: %v", err)
	}

	// Ignore error because default section name is never empty string.
	name := DefaultSection
	if f.options.Insensitive {
		name = strings.ToLower(DefaultSection)
	}
	section, _ := f.NewSection(name)

	// This "last" is not strictly equivalent to "previous one" if current key is not the first nested key
	var isLastValueEmpty bool
	var lastRegularKey *Key

	var line []byte
	var inUnparseableSection bool

	// NOTE: Iterate and increase `currentPeekSize` until
	// the size of the parser buffer is found.
	// TODO(unknwon): When Golang 1.10 is the lowest version supported, replace with `parserBufferSize := p.buf.Size()`.
	parserBufferSize := 0
	// NOTE: Peek 1kb at a time.
	currentPeekSize := 1024

	if f.options.AllowPythonMultilineValues {
		for {
			peekBytes, _ := p.buf.Peek(currentPeekSize)
			peekBytesLength := len(peekBytes)

			if parserBufferSize >= peekBytesLength {
				break
			}

			currentPeekSize *= 2
			parserBufferSize = peekBytesLength
		}
	}

	for !p.isEOF {
		line, err = p.readUntil('\n')
		if err != nil {
			return err
		}

		if f.options.AllowNestedValues &&
			isLastValueEmpty && len(line) > 0 {
			if line[0] == ' ' || line[0] == '\t' {
				lastRegularKey.addNestedValue(string(bytes.TrimSpace(line)))
				continue
			}
		}

		line = bytes.TrimLeftFunc(line, unicode.IsSpace)
		if len(line) == 0 {
			continue
		}

		// Comments
		if line[0] == '#' || line[0] == ';' {
			// Note: we do not care ending line break,
			// it is needed for adding second line,
			// so just clean it once at the end when set to value.
			p.comment.Write(line)
			continue
		}

		// Section
		if line[0] == '[' {
			// Read to the next ']' (TODO: support quoted strings)
			closeIdx := bytes.LastIndexByte(line, ']')
			if closeIdx == -1 {
				return fmt.Errorf("unclosed section: %s", line)
			}

			name := string(line[1:closeIdx])
			section, err = f.NewSection(name)
			if err != nil {
				return err
			}

			comment, has := cleanComment(line[closeIdx+1:])
			if has {
				p.comment.Write(comment)
			}

			section.Comment = strings.TrimSpace(p.comment.String())

			// Reset aotu-counter and comments
			p.comment.Reset()
			p.count = 1

			inUnparseableSection = false
			for i := range f.options.UnparseableSections {
				if f.options.UnparseableSections[i] == name ||
					(f.options.Insensitive && strings.ToLower(f.options.UnparseableSections[i]) == strings.ToLower(name)) {
					inUnparseableSection = true
					continue
				}
			}
			continue
		}

		if inUnparseableSection {
			section.isRawSection = true
			section.rawBody += string(line)
			continue
		}

		kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line)
		if err != nil {
			// Treat as boolean key when desired, and whole line is key name.
			if IsErrDelimiterNotFound(err) {
				switch {
				case f.options.AllowBooleanKeys:
					kname, err := p.readValue(line, parserBufferSize)
					if err != nil {
						return err
					}
					key, err := section.NewBooleanKey(kname)
					if err != nil {
						return err
					}
					key.Comment = strings.TrimSpace(p.comment.String())
					p.comment.Reset()
					continue

				case f.options.SkipUnrecognizableLines:
					continue
				}
			}
			return err
		}

		// Auto increment.
		isAutoIncr := false
		if kname == "-" {
			isAutoIncr = true
			kname = "#" + strconv.Itoa(p.count)
			p.count++
		}

		value, err := p.readValue(line[offset:], parserBufferSize)
		if err != nil {
			return err
		}
		isLastValueEmpty = len(value) == 0

		key, err := section.NewKey(kname, value)
		if err != nil {
			return err
		}
		key.isAutoIncrement = isAutoIncr
		key.Comment = strings.TrimSpace(p.comment.String())
		p.comment.Reset()
		lastRegularKey = key
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Linux Foundation
//
// 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 specs

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 1
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 0
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 0

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-dev"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<commit_msg>version: release v1.0.1<commit_after>// Copyright 2016 The Linux Foundation
//
// 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 specs

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 1
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 0
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 1

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = ""
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<|endoftext|>


<commit_before>package tensor3

import "runtime"

func init() {
	Hints.Threads = uint(runtime.NumCPU()) - 1
	Hints.DefaultChunkSize = 10000
	//runtime.GOMAXPROCS(1)
}

var Hints struct {
	Threads          uint
	ChunkSizeFixed   bool
	DefaultChunkSize uint
}

// selects parallel application of functions to Vectors and Matrices types (slices of Vector and Matrix types).
// this occurs in chunks whose size is controlled by Hints.
var Parallel bool

// selects parallel application of functions to Matrix components,(its Vector fields).
// only improves performance if using costly functions, non of the built-ins are likely to benefit. YRMV.
var ParallelComponents bool

// return a channel of Vectors that are chunks of the passed Vectors
func vectorsInChunks(vs Vectors) chan Vectors {
	c := make(chan Vectors, 1)
	chunkSize := Hints.DefaultChunkSize
	if !Hints.ChunkSizeFixed {
		if cs := uint(len(vs)) / (Hints.Threads + 1); cs > Hints.DefaultChunkSize {
			chunkSize = cs
		}
	}
	lastSplitMax := uint(len(vs))-chunkSize
	go func() {
		var bottom uint
		for top := chunkSize; top < lastSplitMax; top += chunkSize {
			c <- vs[bottom:top]
			bottom = top
		}
		c <- vs[bottom:]
		close(c)
	}()
	return c
}

// return a channel of Matrices that are chunks of the passed Matrices
func matricesInChunks(ms Matrices) chan Matrices {
	c := make(chan Matrices)
	chunkSize := Hints.DefaultChunkSize
	if !Hints.ChunkSizeFixed {
		if cs := uint(len(ms)) / (Hints.Threads + 1); cs > Hints.DefaultChunkSize {
			chunkSize = cs
		}
	}
	lastSplitMax := uint(len(ms))-chunkSize
	go func() {
		var bottom uint
		for top := chunkSize; top < lastSplitMax; top += chunkSize {
			c <- ms[bottom:top]
			bottom = top
		}
		c <- ms[bottom:]
		close(c)
	}()
	return c
}

// return a channel of VectorRefs that are chunks of the passed VectorRefs
func vectorRefsInChunks(vs VectorRefs) chan VectorRefs {
	c := make(chan VectorRefs, 1)
	chunkSize := Hints.DefaultChunkSize
	if !Hints.ChunkSizeFixed {
		if cs := uint(len(vs)) / (Hints.Threads + 1); cs > Hints.DefaultChunkSize {
			chunkSize = cs
		}
	}
	lastSplitMax := uint(len(vs))-chunkSize
	go func() {
		var bottom uint
		for top := chunkSize; top < lastSplitMax; top += chunkSize {
			c <- vs[bottom:top]
			bottom = top
		}
		c <- vs[bottom:]
		close(c)
	}()
	return c
}

<commit_msg>regional chunking<commit_after>package tensor3

import "runtime"

func init() {
	Hints.Threads = uint(runtime.NumCPU()) - 1
	Hints.DefaultChunkSize = 10000
	//runtime.GOMAXPROCS(1)
}

var Hints struct {
	Threads          uint
	ChunkSizeFixed   bool
	DefaultChunkSize uint
}

// selects parallel application of functions to Vectors and Matrices types (slices of Vector and Matrix types).
// this occurs in chunks whose size is controlled by Hints.
var Parallel bool

// selects parallel application of functions to Matrix components,(its Vector fields).
// only improves performance if using costly functions, non of the built-ins are likely to benefit. YRMV.
var ParallelComponents bool

func chunkSize(l uint) uint {
	if !Hints.ChunkSizeFixed {
		if cs := l / (Hints.Threads + 1); cs > Hints.DefaultChunkSize {
			return cs
		}
	}
	return Hints.DefaultChunkSize
}

// return a channel of Vectors that are chunks of the passed Vectors
func vectorsInChunks(vs Vectors) chan Vectors {
	c := make(chan Vectors, 1)
	cs:=chunkSize(uint(len(vs)))
	lastSplitMax := uint(len(vs))-cs
	go func() {
		var bottom uint
		for top := cs; top < lastSplitMax; top += cs {
			c <- vs[bottom:top]
			bottom = top
		}
		c <- vs[bottom:]
		close(c)
	}()
	return c
}

// return a channel of Matrices that are chunks of the passed Matrices
func matricesInChunks(ms Matrices) chan Matrices {
	c := make(chan Matrices)
	cs:=chunkSize(uint(len(ms)))
	lastSplitMax := uint(len(ms))-cs
	go func() {
		var bottom uint
		for top := cs; top < lastSplitMax; top += cs {
			c <- ms[bottom:top]
			bottom = top
		}
		c <- ms[bottom:]
		close(c)
	}()
	return c
}

// return a channel of VectorRefs that are chunks of the passed VectorRefs
func vectorRefsInChunks(vs VectorRefs) chan VectorRefs {
	c := make(chan VectorRefs, 1)
	cs:=chunkSize(uint(len(vs)))
	lastSplitMax := uint(len(vs))-cs
	go func() {
		var bottom uint
		for top := cs; top < lastSplitMax; top += cs {
			c <- vs[bottom:top]
			bottom = top
		}
		c <- vs[bottom:]
		close(c)
	}()
	return c
}

// return a channel of VectorRefs that are chunks of the passed VectorRefs.
// as an optimisation, which some functions might benefit from, the VectorRefs are reordered so that each chunk contains all/only the values within a region. nearby points are MUCH more likely to be in the same chunk.
func vectorRefsInRegionalChunks(vs VectorRefs) chan VectorRefs {
	c := make(chan VectorRefs, 1)
	cs:=chunkSize(uint(len(vs)))
	if cs>uint(len(vs)){
		c <- vs
		close(c)
		return c
	}
	go func() {
		// sample 5% of points to make guess at distribution
		// TODO sample cubed root number of points?
		sp:=make(VectorRefs,len(vs)/20)
		for i:=range(sp){
			sp[i]=vs[i*20]
		}
		// TODO improve this fixed 8-way only scheme. but if less than 8 cores, is there much point?
		// TODO regions to give chunks of a size similar to other chunking functions.
		average:=sp.Sum()
		divisor:=BaseType(len(sp))
		average.x/=divisor
		average.y/=divisor
		average.z/=divisor
		//(&average).Divide(BaseType(len(sp)))
		var chunks [2][2][2]VectorRefs
		for _,v := range(vs){
			if v.x>average.x {
				if v.y>average.y {
					if v.z>average.z {
						chunks[1][1][1]=append(chunks[1][1][1],v)
					}else{
						chunks[1][1][0]=append(chunks[1][1][0],v)
					}
				}else{
					if v.z>average.z {
						chunks[1][0][1]=append(chunks[1][0][1],v)
					}else{
						chunks[1][0][0]=append(chunks[1][0][0],v)
					}
				}
			}else{
				if v.y>average.y {
					if v.z>average.z {
						chunks[0][1][1]=append(chunks[0][1][1],v)
					}else{
						chunks[0][1][0]=append(chunks[0][1][0],v)
					}
				}else{
					if v.z>average.z {
						chunks[0][0][1]=append(chunks[0][0][1],v)
					}else{
						chunks[0][0][0]=append(chunks[0][0][0],v)
					}
				}
			}
		}
		c <- chunks[0][0][0]
		c <- chunks[0][0][1]
		c <- chunks[0][1][0]
		c <- chunks[0][1][1]
		c <- chunks[1][0][0]
		c <- chunks[1][0][1]
		c <- chunks[1][1][0]
		c <- chunks[1][1][1]
		close(c)
	}()
	return c
}

<|endoftext|>


<commit_before>package main

import (
	commander "code.google.com/p/go-commander"

	"log"
)

var Next *commander.Command = &commander.Command{
	UsageLine: "next",
	Short:     "display the current task",
	Long:      `show the currently active task. This will be the most recently added selected task. To select tasks, run "select".`,
	Run: func(cmd *commander.Command, args []string) {
		log.Printf("next: %+v\n", args)
	},
}
<commit_msg>fill out Next<commit_after>package main

import (
	commander "code.google.com/p/go-commander"
	"fmt"
	"github.com/BrianHicks/finch"

	"log"
)

func GetNext(tdb *finch.TaskDB, args []string) (*finch.Task, error) {
	return tdb.GetNextSelected()
}

var Next *commander.Command = &commander.Command{
	UsageLine: "next",
	Short:     "display the current task",
	Long:      `show the currently active task. This will be the most recently added selected task. To select tasks, run "select".`,
	Run: func(cmd *commander.Command, args []string) {
		tdb, err := getTaskDB()
		defer tdb.Close()
		if err != nil {
			log.Fatalf("Error opening Task database: %s\n", err)
		}

		task, err := GetNext(tdb, args)
		if err != nil {
			log.Fatalf("Error getting task: %s\n", err)
		}

		fmt.Printf("%s\n", task.Description)
	},
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package engine

import (
	"regexp"
	"sort"
	"strings"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	jsonw "github.com/keybase/go-jsonw"
)

type TrackList []*libkb.TrackChainLink

func (tl TrackList) Len() int {
	return len(tl)
}

func (tl TrackList) Swap(i, j int) {
	tl[i], tl[j] = tl[j], tl[i]
}

func (tl TrackList) Less(i, j int) bool {
	return strings.ToLower(tl[i].ToDisplayString()) < strings.ToLower(tl[j].ToDisplayString())
}

type ListTrackingEngineArg struct {
	JSON         bool
	Verbose      bool
	Filter       string
	ForAssertion string
}

type ListTrackingEngine struct {
	arg         *ListTrackingEngineArg
	tableResult []keybase1.UserSummary
	jsonResult  string
	libkb.Contextified
}

func NewListTrackingEngine(arg *ListTrackingEngineArg, g *libkb.GlobalContext) *ListTrackingEngine {
	return &ListTrackingEngine{
		arg:          arg,
		Contextified: libkb.NewContextified(g),
	}
}

func (e *ListTrackingEngine) Name() string {
	return "ListTracking"
}

func (e *ListTrackingEngine) Prereqs() Prereqs { return Prereqs{} }

func (e *ListTrackingEngine) RequiredUIs() []libkb.UIKind { return []libkb.UIKind{} }

func (e *ListTrackingEngine) SubConsumers() []libkb.UIConsumer { return nil }

func (e *ListTrackingEngine) Run(ctx *Context) (err error) {

	var arg libkb.LoadUserArg

	if len(e.arg.ForAssertion) > 0 {
		arg = libkb.NewLoadUserByNameArg(e.G(), e.arg.ForAssertion)
	} else {
		arg.Self = true
	}

	err = e.G().GetFullSelfer().WithUser(arg, func(user *libkb.User) error {
		if user == nil {
			return libkb.UserNotFoundError{}
		}

		var trackList TrackList
		var err error
		trackList = user.IDTable().GetTrackList()

		trackList, err = filterRxx(trackList, e.arg.Filter)
		if err != nil {
			return err
		}
		sort.Sort(trackList)

		if e.arg.JSON {
			return e.runJSON(trackList, e.arg.Verbose)
		}
		return e.runTable(trackList)
	})

	return err
}

func filterTracks(trackList TrackList, f func(libkb.TrackChainLink) bool) TrackList {
	ret := TrackList{}
	for _, link := range trackList {
		if f(*link) {
			ret = append(ret, link)
		}
	}
	return ret
}

func filterRxx(trackList TrackList, filter string) (ret TrackList, err error) {
	if len(filter) == 0 {
		return trackList, nil
	}
	rxx, err := regexp.Compile(filter)
	if err != nil {
		return
	}
	return filterTracks(trackList, func(l libkb.TrackChainLink) bool {
		if rxx.MatchString(l.ToDisplayString()) {
			return true
		}
		for _, sb := range l.ToServiceBlocks() {
			_, v := sb.ToKeyValuePair()
			if rxx.MatchString(v) {
				return true
			}
		}
		return false
	}), nil
}

func (e *ListTrackingEngine) linkPGPKeys(link *libkb.TrackChainLink) (res []keybase1.PublicKey) {
	trackedKeys, err := link.GetTrackedKeys()
	if err != nil {
		e.G().Log.Warning("Bad track of %s: %s", link.ToDisplayString(), err)
		return res
	}

	for _, trackedKey := range trackedKeys {
		res = append(res, keybase1.PublicKey{PGPFingerprint: trackedKey.Fingerprint.String()})
	}
	return res
}

func (e *ListTrackingEngine) linkSocialProofs(link *libkb.TrackChainLink) (res []keybase1.TrackProof) {
	for _, sb := range link.ToServiceBlocks() {
		if !sb.IsSocial() {
			continue
		}
		proofType, proofName := sb.ToKeyValuePair()
		res = append(res, keybase1.TrackProof{
			ProofType: proofType,
			ProofName: proofName,
			IdString:  sb.ToIDString(),
		})
	}
	return res
}

func (e *ListTrackingEngine) linkWebProofs(link *libkb.TrackChainLink) (res []keybase1.WebProof) {
	webp := make(map[string]*keybase1.WebProof)
	for _, sb := range link.ToServiceBlocks() {
		if sb.IsSocial() {
			continue
		}
		proofType, proofName := sb.ToKeyValuePair()
		p, ok := webp[proofName]
		if !ok {
			p = &keybase1.WebProof{Hostname: proofName}
			webp[proofName] = p
		}
		p.Protocols = append(p.Protocols, proofType)
	}
	for _, v := range webp {
		res = append(res, *v)
	}
	return res
}

func (e *ListTrackingEngine) runTable(trackList TrackList) error {
	for _, link := range trackList {
		uid, err := link.GetTrackedUID()
		if err != nil {
			return err
		}
		entry := keybase1.UserSummary{
			Username:     link.ToDisplayString(),
			SigIDDisplay: link.GetSigID().ToDisplayString(true),
			TrackTime:    keybase1.ToTime(link.GetCTime()),
			Uid:          keybase1.UID(uid),
		}
		entry.Proofs.PublicKeys = e.linkPGPKeys(link)
		entry.Proofs.Social = e.linkSocialProofs(link)
		entry.Proofs.Web = e.linkWebProofs(link)
		e.tableResult = append(e.tableResult, entry)
	}
	return nil
}

func (e *ListTrackingEngine) runJSON(trackList TrackList, verbose bool) error {
	var tmp []*jsonw.Wrapper
	for _, link := range trackList {
		var rec *jsonw.Wrapper
		var e2 error
		if verbose {
			rec = link.GetPayloadJSON()
		} else if rec, e2 = condenseRecord(link); e2 != nil {
			e.G().Log.Warning("In conversion to JSON: %s", e2)
		}
		if e2 == nil {
			tmp = append(tmp, rec)
		}
	}

	ret := jsonw.NewArray(len(tmp))
	for i, r := range tmp {
		if err := ret.SetIndex(i, r); err != nil {
			return err
		}
	}

	e.jsonResult = ret.MarshalPretty()
	return nil
}

func condenseRecord(l *libkb.TrackChainLink) (*jsonw.Wrapper, error) {
	uid, err := l.GetTrackedUID()
	if err != nil {
		return nil, err
	}

	trackedKeys, err := l.GetTrackedKeys()
	if err != nil {
		return nil, err
	}
	fpsDisplay := make([]string, len(trackedKeys))
	for i, trackedKey := range trackedKeys {
		fpsDisplay[i] = strings.ToUpper(trackedKey.Fingerprint.String())
	}

	un, err := l.GetTrackedUsername()
	if err != nil {
		return nil, err
	}

	rp := l.RemoteKeyProofs()

	out := jsonw.NewDictionary()
	out.SetKey("uid", libkb.UIDWrapper(uid))
	out.SetKey("keys", jsonw.NewString(strings.Join(fpsDisplay, ", ")))
	out.SetKey("ctime", jsonw.NewInt64(l.GetCTime().Unix()))
	out.SetKey("username", jsonw.NewString(un.String()))
	out.SetKey("proofs", rp)

	return out, nil
}

func (e *ListTrackingEngine) TableResult() []keybase1.UserSummary {
	return e.tableResult
}

func (e *ListTrackingEngine) JSONResult() string {
	return e.jsonResult
}
<commit_msg>test user.IDTable() before use (#6468)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package engine

import (
	"regexp"
	"sort"
	"strings"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	jsonw "github.com/keybase/go-jsonw"
)

type TrackList []*libkb.TrackChainLink

func (tl TrackList) Len() int {
	return len(tl)
}

func (tl TrackList) Swap(i, j int) {
	tl[i], tl[j] = tl[j], tl[i]
}

func (tl TrackList) Less(i, j int) bool {
	return strings.ToLower(tl[i].ToDisplayString()) < strings.ToLower(tl[j].ToDisplayString())
}

type ListTrackingEngineArg struct {
	JSON         bool
	Verbose      bool
	Filter       string
	ForAssertion string
}

type ListTrackingEngine struct {
	arg         *ListTrackingEngineArg
	tableResult []keybase1.UserSummary
	jsonResult  string
	libkb.Contextified
}

func NewListTrackingEngine(arg *ListTrackingEngineArg, g *libkb.GlobalContext) *ListTrackingEngine {
	return &ListTrackingEngine{
		arg:          arg,
		Contextified: libkb.NewContextified(g),
	}
}

func (e *ListTrackingEngine) Name() string {
	return "ListTracking"
}

func (e *ListTrackingEngine) Prereqs() Prereqs { return Prereqs{} }

func (e *ListTrackingEngine) RequiredUIs() []libkb.UIKind { return []libkb.UIKind{} }

func (e *ListTrackingEngine) SubConsumers() []libkb.UIConsumer { return nil }

func (e *ListTrackingEngine) Run(ctx *Context) (err error) {

	var arg libkb.LoadUserArg

	if len(e.arg.ForAssertion) > 0 {
		arg = libkb.NewLoadUserByNameArg(e.G(), e.arg.ForAssertion)
	} else {
		arg.Self = true
	}

	err = e.G().GetFullSelfer().WithUser(arg, func(user *libkb.User) error {
		if user == nil {
			return libkb.UserNotFoundError{}
		}

		var trackList TrackList
		var err error
		if idTable := user.IDTable(); idTable != nil {
			trackList = idTable.GetTrackList()
		}

		trackList, err = filterRxx(trackList, e.arg.Filter)
		if err != nil {
			return err
		}
		sort.Sort(trackList)

		if e.arg.JSON {
			return e.runJSON(trackList, e.arg.Verbose)
		}
		return e.runTable(trackList)
	})

	return err
}

func filterTracks(trackList TrackList, f func(libkb.TrackChainLink) bool) TrackList {
	ret := TrackList{}
	for _, link := range trackList {
		if f(*link) {
			ret = append(ret, link)
		}
	}
	return ret
}

func filterRxx(trackList TrackList, filter string) (ret TrackList, err error) {
	if len(filter) == 0 {
		return trackList, nil
	}
	rxx, err := regexp.Compile(filter)
	if err != nil {
		return
	}
	return filterTracks(trackList, func(l libkb.TrackChainLink) bool {
		if rxx.MatchString(l.ToDisplayString()) {
			return true
		}
		for _, sb := range l.ToServiceBlocks() {
			_, v := sb.ToKeyValuePair()
			if rxx.MatchString(v) {
				return true
			}
		}
		return false
	}), nil
}

func (e *ListTrackingEngine) linkPGPKeys(link *libkb.TrackChainLink) (res []keybase1.PublicKey) {
	trackedKeys, err := link.GetTrackedKeys()
	if err != nil {
		e.G().Log.Warning("Bad track of %s: %s", link.ToDisplayString(), err)
		return res
	}

	for _, trackedKey := range trackedKeys {
		res = append(res, keybase1.PublicKey{PGPFingerprint: trackedKey.Fingerprint.String()})
	}
	return res
}

func (e *ListTrackingEngine) linkSocialProofs(link *libkb.TrackChainLink) (res []keybase1.TrackProof) {
	for _, sb := range link.ToServiceBlocks() {
		if !sb.IsSocial() {
			continue
		}
		proofType, proofName := sb.ToKeyValuePair()
		res = append(res, keybase1.TrackProof{
			ProofType: proofType,
			ProofName: proofName,
			IdString:  sb.ToIDString(),
		})
	}
	return res
}

func (e *ListTrackingEngine) linkWebProofs(link *libkb.TrackChainLink) (res []keybase1.WebProof) {
	webp := make(map[string]*keybase1.WebProof)
	for _, sb := range link.ToServiceBlocks() {
		if sb.IsSocial() {
			continue
		}
		proofType, proofName := sb.ToKeyValuePair()
		p, ok := webp[proofName]
		if !ok {
			p = &keybase1.WebProof{Hostname: proofName}
			webp[proofName] = p
		}
		p.Protocols = append(p.Protocols, proofType)
	}
	for _, v := range webp {
		res = append(res, *v)
	}
	return res
}

func (e *ListTrackingEngine) runTable(trackList TrackList) error {
	for _, link := range trackList {
		uid, err := link.GetTrackedUID()
		if err != nil {
			return err
		}
		entry := keybase1.UserSummary{
			Username:     link.ToDisplayString(),
			SigIDDisplay: link.GetSigID().ToDisplayString(true),
			TrackTime:    keybase1.ToTime(link.GetCTime()),
			Uid:          keybase1.UID(uid),
		}
		entry.Proofs.PublicKeys = e.linkPGPKeys(link)
		entry.Proofs.Social = e.linkSocialProofs(link)
		entry.Proofs.Web = e.linkWebProofs(link)
		e.tableResult = append(e.tableResult, entry)
	}
	return nil
}

func (e *ListTrackingEngine) runJSON(trackList TrackList, verbose bool) error {
	var tmp []*jsonw.Wrapper
	for _, link := range trackList {
		var rec *jsonw.Wrapper
		var e2 error
		if verbose {
			rec = link.GetPayloadJSON()
		} else if rec, e2 = condenseRecord(link); e2 != nil {
			e.G().Log.Warning("In conversion to JSON: %s", e2)
		}
		if e2 == nil {
			tmp = append(tmp, rec)
		}
	}

	ret := jsonw.NewArray(len(tmp))
	for i, r := range tmp {
		if err := ret.SetIndex(i, r); err != nil {
			return err
		}
	}

	e.jsonResult = ret.MarshalPretty()
	return nil
}

func condenseRecord(l *libkb.TrackChainLink) (*jsonw.Wrapper, error) {
	uid, err := l.GetTrackedUID()
	if err != nil {
		return nil, err
	}

	trackedKeys, err := l.GetTrackedKeys()
	if err != nil {
		return nil, err
	}
	fpsDisplay := make([]string, len(trackedKeys))
	for i, trackedKey := range trackedKeys {
		fpsDisplay[i] = strings.ToUpper(trackedKey.Fingerprint.String())
	}

	un, err := l.GetTrackedUsername()
	if err != nil {
		return nil, err
	}

	rp := l.RemoteKeyProofs()

	out := jsonw.NewDictionary()
	out.SetKey("uid", libkb.UIDWrapper(uid))
	out.SetKey("keys", jsonw.NewString(strings.Join(fpsDisplay, ", ")))
	out.SetKey("ctime", jsonw.NewInt64(l.GetCTime().Unix()))
	out.SetKey("username", jsonw.NewString(un.String()))
	out.SetKey("proofs", rp)

	return out, nil
}

func (e *ListTrackingEngine) TableResult() []keybase1.UserSummary {
	return e.tableResult
}

func (e *ListTrackingEngine) JSONResult() string {
	return e.jsonResult
}
<|endoftext|>


<commit_before>package operators

import "testing"

func TestEqualSlicesOfByte(t *testing.T) {
	// Equal.
	if !EqualSlicesOfByte([]byte{}, []byte{}) {
		t.Errorf("EqualSlicesOfByte([]byte): [] == []")
	}
	if !EqualSlicesOfByte([]byte{1, 2, 3}, []byte{1, 2, 3}) {
		t.Errorf("EqualSlicesOfByte([]byte): [1, 2, 3] == [1, 2, 3]")
	}
	// Not equal.
	if EqualSlicesOfByte([]byte{1, 2, 3}, []byte{}) {
		t.Errorf("EqualSlicesOfByte([]byte): [1, 2, 3] != []")
	}
	if EqualSlicesOfByte([]byte{1, 2, 3}, []byte{4, 5, 6}) {
		t.Errorf("EqualSlicesOfByte([]byte): [1, 2, 3] != [4, 5, 6]")
	}
}

func TestEqualSlicesOf2Byte(t *testing.T) {
	// Equal.
	if !EqualSlicesOf2Byte([][]byte{}, [][]byte{}) {
		t.Errorf("EqualSlicesOf2Byte([][]byte): [][] == [][]")
	}
	if !EqualSlicesOf2Byte([][]byte{{1, 2}, {3, 4}}, [][]byte{{1, 2}, {3, 4}}) {
		t.Errorf("EqualSlicesOf2Byte([][]byte): [1, 2][3, 4] == [1, 2][3, 4]")
	}
	// Not equal.
	if EqualSlicesOf2Byte([][]byte{{1, 2}, {3, 4}}, [][]byte{}) {
		t.Errorf("EqualSlicesOf2Byte([][]byte): [1, 2][3, 4] == []")
	}
	if EqualSlicesOf2Byte([][]byte{{1, 2}}, [][]byte{{1, 2}, {3, 4}}) {
		t.Errorf("EqualSlicesOf2Byte([][]byte): [1, 2][3, 4] == [1, 2][3, 4]")
	}
}

func TestEqualSlicesOfInt(t *testing.T) {
	// Equal.
	if !EqualSlicesOfInt([]int{}, []int{}) {
		t.Errorf("EqualSlicesOfInt([]int): [] == []")
	}
	if !EqualSlicesOfInt([]int{1, 2, 3}, []int{1, 2, 3}) {
		t.Errorf("EqualSlicesOfInt([]int): [1, 2, 3] == [1, 2, 3]")
	}
	// Not equal.
	if EqualSlicesOfInt([]int{1, 2, 3}, []int{}) {
		t.Errorf("EqualSlicesOfInt([]int): [1, 2, 3] != []")
	}
	if EqualSlicesOfInt([]int{1, 2, 3}, []int{4, 5, 6}) {
		t.Errorf("EqualSlicesOfInt([]int): [1, 2, 3] != [4, 5, 6]")
	}
}

func TestEqualSliceOfString(t *testing.T) {
	// Equal.
	if !EqualSlicesOfString([]string{}, []string{}) {
		t.Errorf("EqualSlicesOfString([]string): [] == []")
	}
	if !EqualSlicesOfString([]string{"1", "2", "3"}, []string{"1", "2", "3"}) {
		t.Errorf("EqualSlicesOfString([]string): [1, 2, 3] == [1, 2, 3]")
	}
	// Not equal.
	if EqualSlicesOfString([]string{"1", "2", "3"}, []string{}) {
		t.Errorf("EqualSlicesOfString([]string): [1, 2, 3] != []")
	}
	if EqualSlicesOfString([]string{"1", "2", "3"}, []string{"4", "5", "6"}) {
		t.Errorf("EqualSlicesOfString([]string): [1, 2, 3] != [4, 5, 6]")
	}
}
<commit_msg>added two more test cases for TestEqualSlicesOfByte()<commit_after>package operators

import "testing"

func TestEqualSlicesOfByte(t *testing.T) {
	// Equal.
	if !EqualSlicesOfByte(nil, nil) {
		t.Errorf("EqualSlicesOfByte([]byte): nil == nil")
	}
	if !EqualSlicesOfByte([]byte{}, []byte{}) {
		t.Errorf("EqualSlicesOfByte([]byte): [] == []")
	}
	if !EqualSlicesOfByte([]byte{1, 2, 3}, []byte{1, 2, 3}) {
		t.Errorf("EqualSlicesOfByte([]byte): [1, 2, 3] == [1, 2, 3]")
	}
	// Not equal.
	if EqualSlicesOfByte([]byte{1, 2, 3}, nil) {
		t.Errorf("EqualSlicesOfByte([]byte): [1, 2, 3] != nil")
	}
	if EqualSlicesOfByte([]byte{1, 2, 3}, []byte{}) {
		t.Errorf("EqualSlicesOfByte([]byte): [1, 2, 3] != []")
	}
	if EqualSlicesOfByte([]byte{1, 2, 3}, []byte{4, 5, 6}) {
		t.Errorf("EqualSlicesOfByte([]byte): [1, 2, 3] != [4, 5, 6]")
	}
}

func TestEqualSlicesOf2Byte(t *testing.T) {
	// Equal.
	if !EqualSlicesOf2Byte([][]byte{}, [][]byte{}) {
		t.Errorf("EqualSlicesOf2Byte([][]byte): [][] == [][]")
	}
	if !EqualSlicesOf2Byte([][]byte{{1, 2}, {3, 4}}, [][]byte{{1, 2}, {3, 4}}) {
		t.Errorf("EqualSlicesOf2Byte([][]byte): [1, 2][3, 4] == [1, 2][3, 4]")
	}
	// Not equal.
	if EqualSlicesOf2Byte([][]byte{{1, 2}, {3, 4}}, [][]byte{}) {
		t.Errorf("EqualSlicesOf2Byte([][]byte): [1, 2][3, 4] == []")
	}
	if EqualSlicesOf2Byte([][]byte{{1, 2}}, [][]byte{{1, 2}, {3, 4}}) {
		t.Errorf("EqualSlicesOf2Byte([][]byte): [1, 2][3, 4] == [1, 2][3, 4]")
	}
}

func TestEqualSlicesOfInt(t *testing.T) {
	// Equal.
	if !EqualSlicesOfInt([]int{}, []int{}) {
		t.Errorf("EqualSlicesOfInt([]int): [] == []")
	}
	if !EqualSlicesOfInt([]int{1, 2, 3}, []int{1, 2, 3}) {
		t.Errorf("EqualSlicesOfInt([]int): [1, 2, 3] == [1, 2, 3]")
	}
	// Not equal.
	if EqualSlicesOfInt([]int{1, 2, 3}, []int{}) {
		t.Errorf("EqualSlicesOfInt([]int): [1, 2, 3] != []")
	}
	if EqualSlicesOfInt([]int{1, 2, 3}, []int{4, 5, 6}) {
		t.Errorf("EqualSlicesOfInt([]int): [1, 2, 3] != [4, 5, 6]")
	}
}

func TestEqualSliceOfString(t *testing.T) {
	// Equal.
	if !EqualSlicesOfString([]string{}, []string{}) {
		t.Errorf("EqualSlicesOfString([]string): [] == []")
	}
	if !EqualSlicesOfString([]string{"1", "2", "3"}, []string{"1", "2", "3"}) {
		t.Errorf("EqualSlicesOfString([]string): [1, 2, 3] == [1, 2, 3]")
	}
	// Not equal.
	if EqualSlicesOfString([]string{"1", "2", "3"}, []string{}) {
		t.Errorf("EqualSlicesOfString([]string): [1, 2, 3] != []")
	}
	if EqualSlicesOfString([]string{"1", "2", "3"}, []string{"4", "5", "6"}) {
		t.Errorf("EqualSlicesOfString([]string): [1, 2, 3] != [4, 5, 6]")
	}
}
<|endoftext|>


<commit_before>package db

import (
	"fmt"
	"regexp"
	"time"

	"github.com/koding/logging"
)

func NewGormLogger(log logging.Logger) *gormLogger {
	return &gormLogger{log: log}
}

// custom logger for gorm
type gormLogger struct {
	log logging.Logger
}

// Format log
var sqlRegexp = regexp.MustCompile(`(\$\d+)|\?`)

// implement gorm logging interface
func (g *gormLogger) Print(v ...interface{}) {
	messages := []interface{}{}

	// taken from gorm log file
	if len(v) > 4 {
		messages = append(messages, fmt.Sprintf(" [%.2fms] ", float64(v[2].(time.Duration).Nanoseconds()/1e4)/100.0))
		messages = append(messages, fmt.Sprintf(sqlRegexp.ReplaceAllString(v[3].(string), "'%v'"), v[4].([]interface{})...))
	} else {
		for _, message := range v {
			messages = append(messages, fmt.Sprintf("%s", message))
		}
	}

	format := ""
	for i := 0; i < len(messages); i++ {
		format += "%s"
	}

	g.log.Debug(format, messages...)
}
<commit_msg>Social: remove extra space from db log<commit_after>package db

import (
	"fmt"
	"regexp"
	"time"

	"github.com/koding/logging"
)

func NewGormLogger(log logging.Logger) *gormLogger {
	return &gormLogger{log: log}
}

// custom logger for gorm
type gormLogger struct {
	log logging.Logger
}

// Format log
var sqlRegexp = regexp.MustCompile(`(\$\d+)|\?`)

// implement gorm logging interface
func (g *gormLogger) Print(v ...interface{}) {
	messages := []interface{}{}

	// taken from gorm log file
	if len(v) > 4 {
		messages = append(messages, fmt.Sprintf("[%.2fms] ", float64(v[2].(time.Duration).Nanoseconds()/1e4)/100.0))
		messages = append(messages, fmt.Sprintf(sqlRegexp.ReplaceAllString(v[3].(string), "'%v'"), v[4].([]interface{})...))
	} else {
		for _, message := range v {
			messages = append(messages, fmt.Sprintf("%s", message))
		}
	}

	format := ""
	for i := 0; i < len(messages); i++ {
		format += "%s"
	}

	g.log.Debug(format, messages...)
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package systests

import (
	"bytes"
	"fmt"
	"testing"
	"time"

	"github.com/keybase/client/go/client"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/client/go/service"
	rpc "github.com/keybase/go-framed-msgpack-rpc"
	gregor1 "github.com/keybase/gregor/protocol/gregor1"
	context "golang.org/x/net/context"
)

type electronMock struct {
	libkb.Contextified
	errCh   chan error
	stateCh chan keybase1.PushStateArg
	oobmCh  chan gregor1.OutOfBandMessage
}

func (e *electronMock) PushState(ctx context.Context, a keybase1.PushStateArg) (err error) {
	e.G().Log.Debug("electronMock::PushState: %#v\n", a)
	e.stateCh <- a
	return nil
}

func (e *electronMock) PushOutOfBandMessages(_ context.Context, msgs []gregor1.OutOfBandMessage) error {
	for _, m := range msgs {
		e.oobmCh <- m
	}
	return nil
}

func newElectronMock(g *libkb.GlobalContext) *electronMock {
	return &electronMock{
		Contextified: libkb.NewContextified(g),
		errCh:        make(chan error, 1),
		stateCh:      make(chan keybase1.PushStateArg, 10),
		oobmCh:       make(chan gregor1.OutOfBandMessage, 10),
	}
}

func TestGregorForwardToElectron(t *testing.T) {
	tc := setupTest(t, "gregor")
	defer tc.Cleanup()
	tc1 := cloneContext(tc)

	svc := service.NewService(tc.G, false)
	startCh := svc.GetStartChannel()
	stopCh := make(chan error)
	go func() {
		tc.G.Log.Debug("+ Service.Run")
		err := svc.Run()
		tc.G.Log.Debug("- Service.Run")
		if err != nil {
			t.Logf("Running the service produced an error: %v", err)
		}
		stopCh <- err
	}()

	userInfo := randomUser("grgr")
	sui := signupUI{
		info:         userInfo,
		Contextified: libkb.NewContextified(tc.G),
	}
	tc.G.SetUI(&sui)
	signup := client.NewCmdSignupRunner(tc.G)
	signup.SetTest()

	// Wait for the server to start up
	<-startCh

	if err := signup.Run(); err != nil {
		t.Fatal(err)
	}
	tc.G.Log.Debug("Login State: %v", tc.G.LoginState())

	var err error
	check := func() {
		if err != nil {
			t.Fatal(err)
		}
	}
	cli, xp, err := client.GetRPCClientWithContext(tc1.G)
	srv := rpc.NewServer(xp, nil)
	em := newElectronMock(tc.G)
	err = srv.Register(keybase1.GregorUIProtocol(em))
	check()
	ncli := keybase1.DelegateUiCtlClient{Cli: cli}

	// Spin until gregor comes up; it should come up after signup
	var ok bool
	for i := 0; !ok && i < 40; i++ {
		if ok = svc.HasGregor(); !ok {
			time.Sleep(50 * time.Millisecond)
		} else {
			tc.G.Log.Debug("spinning, waiting for gregor to come up (attempt %d)", i)
		}
	}
	if !ok {
		t.Fatal("Gregor never came up after we signed up")
	}

	err = ncli.RegisterGregorFirehose(context.TODO())
	check()

	select {
	case a := <-em.stateCh:
		if a.Reason != keybase1.PushReason_RECONNECTED {
			t.Fatal(fmt.Sprintf("got wrong reason: %v", a.Reason))
		}
		if d := len(a.State.Items_); d != 0 {
			t.Fatal(fmt.Sprintf("Wrong number of items in state -- should have 0, but got %d", d))
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got a reconnect message")
	}

	msgID, err := svc.GregorInject("foo", []byte("bar"))
	check()
	err = svc.GregorInjectOutOfBandMessage("baz", []byte("bip"))
	check()

	checkState := func(s gregor1.State) {
		if n := len(s.Items_); n != 1 {
			t.Errorf("Expected one item back; got %d", n)
			return
		}
		i := s.Items_[0]
		if !bytes.Equal(i.Md_.MsgID_.Bytes(), msgID.Bytes()) {
			t.Error("Wrong gregor message ID received")
		}
		if i.Item_.Category_.String() != "foo" {
			t.Error("Wrong gregor category")
		}
		if string(i.Item_.Body_.Bytes()) != "bar" {
			t.Error("Wrong gregor body")
		}
	}

	select {
	case pushArg := <-em.stateCh:
		checkState(pushArg.State)
		if pushArg.Reason != keybase1.PushReason_NEW_DATA {
			t.Errorf("wrong reason for push: %v", pushArg.Reason)
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got an IBM")
	}

	select {
	case oobm := <-em.oobmCh:
		if oobm.System_ != "baz" {
			t.Fatalf("Got wrong OOBM system: %s", oobm.System_)
		}
		if s := string(oobm.Body_); s != "bip" {
			t.Fatalf("Got wrong OOBM body: %s", s)
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got an OOBM")
	}

	svc.SimulateGregorCrashForTesting()
	select {
	case pushArg := <-em.stateCh:
		checkState(pushArg.State)
		if pushArg.Reason != keybase1.PushReason_RECONNECTED {
			t.Errorf("wrong reason for push: %v", pushArg.Reason)
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got an IBM")
	}

	gcli := keybase1.GregorClient{Cli: cli}
	state, err := gcli.GetState(context.TODO())
	check()
	checkState(state)

	if err := client.CtlServiceStop(tc.G); err != nil {
		t.Fatal(err)
	}
	// If the server failed, it's also an error
	if err := <-stopCh; err != nil {
		t.Fatal(err)
	}

}
<commit_msg>filter pubsub items<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package systests

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/keybase/client/go/client"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/client/go/service"
	rpc "github.com/keybase/go-framed-msgpack-rpc"
	gregor1 "github.com/keybase/gregor/protocol/gregor1"
	context "golang.org/x/net/context"
)

type electronMock struct {
	libkb.Contextified
	errCh   chan error
	stateCh chan keybase1.PushStateArg
	oobmCh  chan gregor1.OutOfBandMessage
}

func (e *electronMock) PushState(ctx context.Context, a keybase1.PushStateArg) (err error) {
	e.G().Log.Debug("electronMock::PushState: %#v\n", a)
	e.stateCh <- a
	return nil
}

func (e *electronMock) PushOutOfBandMessages(_ context.Context, msgs []gregor1.OutOfBandMessage) error {
	for _, m := range msgs {
		e.oobmCh <- m
	}
	return nil
}

func newElectronMock(g *libkb.GlobalContext) *electronMock {
	return &electronMock{
		Contextified: libkb.NewContextified(g),
		errCh:        make(chan error, 1),
		stateCh:      make(chan keybase1.PushStateArg, 10),
		oobmCh:       make(chan gregor1.OutOfBandMessage, 10),
	}
}

func filterPubsubdItems(items []gregor1.ItemAndMetadata) (res []gregor1.ItemAndMetadata) {
	for _, i := range items {
		if !strings.HasPrefix(i.Category().String(), "user.") {
			res = append(res, i)
		}
	}
	return
}

func TestGregorForwardToElectron(t *testing.T) {
	tc := setupTest(t, "gregor")
	defer tc.Cleanup()
	tc1 := cloneContext(tc)

	svc := service.NewService(tc.G, false)
	startCh := svc.GetStartChannel()
	stopCh := make(chan error)
	go func() {
		tc.G.Log.Debug("+ Service.Run")
		err := svc.Run()
		tc.G.Log.Debug("- Service.Run")
		if err != nil {
			t.Logf("Running the service produced an error: %v", err)
		}
		stopCh <- err
	}()

	userInfo := randomUser("grgr")
	sui := signupUI{
		info:         userInfo,
		Contextified: libkb.NewContextified(tc.G),
	}
	tc.G.SetUI(&sui)
	signup := client.NewCmdSignupRunner(tc.G)
	signup.SetTest()

	// Wait for the server to start up
	<-startCh

	if err := signup.Run(); err != nil {
		t.Fatal(err)
	}
	tc.G.Log.Debug("Login State: %v", tc.G.LoginState())

	var err error
	check := func() {
		if err != nil {
			t.Fatal(err)
		}
	}
	cli, xp, err := client.GetRPCClientWithContext(tc1.G)
	srv := rpc.NewServer(xp, nil)
	em := newElectronMock(tc.G)
	err = srv.Register(keybase1.GregorUIProtocol(em))
	check()
	ncli := keybase1.DelegateUiCtlClient{Cli: cli}

	// Spin until gregor comes up; it should come up after signup
	var ok bool
	for i := 0; !ok && i < 40; i++ {
		if ok = svc.HasGregor(); !ok {
			time.Sleep(50 * time.Millisecond)
		} else {
			tc.G.Log.Debug("spinning, waiting for gregor to come up (attempt %d)", i)
		}
	}
	if !ok {
		t.Fatal("Gregor never came up after we signed up")
	}

	err = ncli.RegisterGregorFirehose(context.TODO())
	check()

	select {
	case a := <-em.stateCh:
		if a.Reason != keybase1.PushReason_RECONNECTED {
			t.Fatal(fmt.Sprintf("got wrong reason: %v", a.Reason))
		}
		if d := len(filterPubsubdItems(a.State.Items_)); d != 0 {
			t.Fatal(fmt.Sprintf("Wrong number of items in state -- should have 0, but got %d", d))
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got a reconnect message")
	}

	msgID, err := svc.GregorInject("foo", []byte("bar"))
	check()
	err = svc.GregorInjectOutOfBandMessage("baz", []byte("bip"))
	check()

	checkState := func(s gregor1.State) {
		items := filterPubsubdItems(s.Items_)
		if n := len(items); n != 1 {
			t.Errorf("Expected one item back; got %d", n)
			return
		}
		i := items[0]
		if !bytes.Equal(i.Md_.MsgID_.Bytes(), msgID.Bytes()) {
			t.Error("Wrong gregor message ID received")
		}
		if i.Item_.Category_.String() != "foo" {
			t.Error("Wrong gregor category")
		}
		if string(i.Item_.Body_.Bytes()) != "bar" {
			t.Error("Wrong gregor body")
		}
	}

	select {
	case pushArg := <-em.stateCh:
		checkState(pushArg.State)
		if pushArg.Reason != keybase1.PushReason_NEW_DATA {
			t.Errorf("wrong reason for push: %v", pushArg.Reason)
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got an IBM")
	}

	select {
	case oobm := <-em.oobmCh:
		if oobm.System_ != "baz" {
			t.Fatalf("Got wrong OOBM system: %s", oobm.System_)
		}
		if s := string(oobm.Body_); s != "bip" {
			t.Fatalf("Got wrong OOBM body: %s", s)
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got an OOBM")
	}

	svc.SimulateGregorCrashForTesting()
	select {
	case pushArg := <-em.stateCh:
		checkState(pushArg.State)
		if pushArg.Reason != keybase1.PushReason_RECONNECTED {
			t.Errorf("wrong reason for push: %v", pushArg.Reason)
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got an IBM")
	}

	gcli := keybase1.GregorClient{Cli: cli}
	state, err := gcli.GetState(context.TODO())
	check()
	checkState(state)

	if err := client.CtlServiceStop(tc.G); err != nil {
		t.Fatal(err)
	}
	// If the server failed, it's also an error
	if err := <-stopCh; err != nil {
		t.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package googlechat

import (
	"bytes"
	"context"
	"encoding/json"
	"log"
	"net/http"
	"strings"
	"time"

	"cloud.google.com/go/pubsub"
	"github.com/go-chat-bot/bot"
	"golang.org/x/oauth2/google"
)

const (
	chatAuthScope = "https://www.googleapis.com/auth/chat.bot"
	apiEndpoint   = "https://chat.googleapis.com/v1/"
	protocol      = "googlechat"
	server        = "chat.google.com"
)

var (
	httpChatClient *http.Client
	b              *bot.Bot
)

// Config must contain basic configuration for the bot to be able to work
type Config struct {
	PubSubProject    string
	TopicName        string
	SubscriptionName string
	Token            string
	WelcomeMessage   string
}

func responseHandler(target string, message string, sender *bot.User) {
	var space, thread string

	// this define thread in the reply if we can so we don't alwayus start new
	targets := strings.Split(target, ":")
	if len(targets) < 2 {
		space = target
	} else {
		space = targets[0]
		thread = targets[1]
	}

	reply, err := json.Marshal(&ReplyMessage{
		Text: message,
		Thread: &ReplyThread{
			Name: thread}})

	log.Printf("Replying: space: %s thread: %s message: %s\n",
		space, thread, message)
	resp, err := httpChatClient.Post(apiEndpoint+space+"/messages",
		"application/json",
		bytes.NewReader(reply))
	if err != nil {
		log.Printf("Error posting reply: %v", err)
	}

	log.Printf("Response: %s\n", resp.Status)
}

// Run reads the config, establishes OAuth connection & Pub/Sub subscription to
// the message queue
func Run(config *Config) {
	var err error
	ctx := context.Background()
	httpChatClient, err = google.DefaultClient(ctx, chatAuthScope)
	if err != nil {
		log.Printf("Error setting http client: %v\n", err)
		return
	}

	client, err := pubsub.NewClient(ctx, config.PubSubProject)
	if err != nil {
		log.Printf("Error creating client: %v\n", err)
		return
	}

	topic := client.Topic(config.TopicName)

	// Create a new subscription to the previously created topic
	// with the given name.
	sub := client.Subscription(config.SubscriptionName)
	ok, err := sub.Exists(ctx)
	if err != nil {
		log.Printf("Error getting subscription: %v\n", err)
		return
	}
	if !ok {
		// Subscription doesn't exist.
		sub, err = client.CreateSubscription(ctx, config.SubscriptionName,
			pubsub.SubscriptionConfig{
				Topic:       topic,
				AckDeadline: 10 * time.Second,
			})
		if err != nil {
			log.Printf("Error subscribing: %v\n", err)
			return
		}
	}

	b = bot.New(&bot.Handlers{
		Response: responseHandler,
	},
		&bot.Config{
			Protocol: protocol,
			Server:   server,
		},
	)

	err = sub.Receive(context.Background(),
		func(ctx context.Context, m *pubsub.Message) {
			var msg ChatMessage
			err = json.Unmarshal(m.Data, &msg)
			if err != nil {
				log.Printf("Failed message unmarshal(%v): %s\n", err, m.Data)
				m.Ack()
				return
			}
			if msg.Token != config.Token {
				log.Printf("Failed to verify token: %s", msg.Token)
				m.Ack()
				return
			}

			log.Printf("Space: %s (%s)\n", msg.Space.Name, msg.Space.DisplayName)
			log.Printf("Message type: %s\n", msg.Type)
			log.Printf("From: %s (%s)\n", msg.User.Name, msg.User.DisplayName)
			switch msg.Type {
			case "ADDED_TO_SPACE":
				if config.WelcomeMessage != "" {
					log.Printf("Sending welcome message to %s\n", msg.Space.Name)
					b.SendMessage(OutgoingMessage{
						Target:  msg.Space.Name,
						Message: config.WelcomeMessage,
					})
				}
			case "REMOVED_FROM_SPACE":
				break
			case "MESSAGE":
				log.Printf("Message: %s\n", msg.Message.ArgumentText)
				b.MessageReceived(
					&bot.ChannelData{
						Protocol:  protocol,
						Server:    server,
						HumanName: msg.Space.DisplayName,
						Channel:   msg.Space.Name + ":" + msg.Message.Thread.Name,
						IsPrivate: msg.Space.Type == "DM",
					},
					&bot.Message{
						Text:     msg.Message.ArgumentText,
						IsAction: false,
					},
					&bot.User{
						ID:       msg.User.Name,
						Nick:     msg.User.DisplayName,
						RealName: msg.User.DisplayName,
					})
			}

			m.Ack()
		})
	if err != nil {
		log.Printf("Error setting up receiving: %v\n", err)
		return
	}
	// Wait indefinetely
	select {}
}
<commit_msg>fix the build (#118)<commit_after>package googlechat

import (
	"bytes"
	"context"
	"encoding/json"
	"log"
	"net/http"
	"strings"
	"time"

	"cloud.google.com/go/pubsub"
	"github.com/go-chat-bot/bot"
	"golang.org/x/oauth2/google"
)

const (
	chatAuthScope = "https://www.googleapis.com/auth/chat.bot"
	apiEndpoint   = "https://chat.googleapis.com/v1/"
	protocol      = "googlechat"
	server        = "chat.google.com"
)

var (
	httpChatClient *http.Client
	b              *bot.Bot
)

// Config must contain basic configuration for the bot to be able to work
type Config struct {
	PubSubProject    string
	TopicName        string
	SubscriptionName string
	Token            string
	WelcomeMessage   string
}

func responseHandler(target string, message string, sender *bot.User) {
	var space, thread string

	// this define thread in the reply if we can so we don't alwayus start new
	targets := strings.Split(target, ":")
	if len(targets) < 2 {
		space = target
	} else {
		space = targets[0]
		thread = targets[1]
	}

	reply, err := json.Marshal(&ReplyMessage{
		Text: message,
		Thread: &ReplyThread{
			Name: thread}})

	log.Printf("Replying: space: %s thread: %s message: %s\n",
		space, thread, message)
	resp, err := httpChatClient.Post(apiEndpoint+space+"/messages",
		"application/json",
		bytes.NewReader(reply))
	if err != nil {
		log.Printf("Error posting reply: %v", err)
	}

	log.Printf("Response: %s\n", resp.Status)
}

// Run reads the config, establishes OAuth connection & Pub/Sub subscription to
// the message queue
func Run(config *Config) {
	var err error
	ctx := context.Background()
	httpChatClient, err = google.DefaultClient(ctx, chatAuthScope)
	if err != nil {
		log.Printf("Error setting http client: %v\n", err)
		return
	}

	client, err := pubsub.NewClient(ctx, config.PubSubProject)
	if err != nil {
		log.Printf("Error creating client: %v\n", err)
		return
	}

	topic := client.Topic(config.TopicName)

	// Create a new subscription to the previously created topic
	// with the given name.
	sub := client.Subscription(config.SubscriptionName)
	ok, err := sub.Exists(ctx)
	if err != nil {
		log.Printf("Error getting subscription: %v\n", err)
		return
	}
	if !ok {
		// Subscription doesn't exist.
		sub, err = client.CreateSubscription(ctx, config.SubscriptionName,
			pubsub.SubscriptionConfig{
				Topic:       topic,
				AckDeadline: 10 * time.Second,
			})
		if err != nil {
			log.Printf("Error subscribing: %v\n", err)
			return
		}
	}

	b = bot.New(&bot.Handlers{
		Response: responseHandler,
	},
		&bot.Config{
			Protocol: protocol,
			Server:   server,
		},
	)

	err = sub.Receive(context.Background(),
		func(ctx context.Context, m *pubsub.Message) {
			var msg ChatMessage
			err = json.Unmarshal(m.Data, &msg)
			if err != nil {
				log.Printf("Failed message unmarshal(%v): %s\n", err, m.Data)
				m.Ack()
				return
			}
			if msg.Token != config.Token {
				log.Printf("Failed to verify token: %s", msg.Token)
				m.Ack()
				return
			}

			log.Printf("Space: %s (%s)\n", msg.Space.Name, msg.Space.DisplayName)
			log.Printf("Message type: %s\n", msg.Type)
			log.Printf("From: %s (%s)\n", msg.User.Name, msg.User.DisplayName)
			switch msg.Type {
			case "ADDED_TO_SPACE":
				if config.WelcomeMessage != "" {
					log.Printf("Sending welcome message to %s\n", msg.Space.Name)
					b.SendMessage(bot.OutgoingMessage{
						Target:  msg.Space.Name,
						Message: config.WelcomeMessage,
					})
				}
			case "REMOVED_FROM_SPACE":
				break
			case "MESSAGE":
				log.Printf("Message: %s\n", msg.Message.ArgumentText)
				b.MessageReceived(
					&bot.ChannelData{
						Protocol:  protocol,
						Server:    server,
						HumanName: msg.Space.DisplayName,
						Channel:   msg.Space.Name + ":" + msg.Message.Thread.Name,
						IsPrivate: msg.Space.Type == "DM",
					},
					&bot.Message{
						Text:     msg.Message.ArgumentText,
						IsAction: false,
					},
					&bot.User{
						ID:       msg.User.Name,
						Nick:     msg.User.DisplayName,
						RealName: msg.User.DisplayName,
					})
			}

			m.Ack()
		})
	if err != nil {
		log.Printf("Error setting up receiving: %v\n", err)
		return
	}
	// Wait indefinetely
	select {}
}
<|endoftext|>