<commit_before>// Most functions in package parse are exported on the off change that someone would like to use them as library functions
// in their own project
package parse

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"reflect"

	"flag"

	"go/format"

	"github.com/alistanis/st/flags"
	"github.com/alistanis/st/sterrors"
)

var (
	lastTypeName string
)

// Takes all provided arguments, iterates over them, stats them, and then inspects source files
func ParseAndProcess() error {
	for _, p := range flag.Args() {
		fi, err := os.Stat(p)
		if err != nil {
			return err
		}
		if fi.IsDir() {
			return fmt.Errorf("Cannot use a directory as a path. Path: %s", fi.Name())
		}
		f, data, err := ParseFile(p)
		if err != nil {
			return err
		}
		data, err = Inspect(f, data)
		if err != nil {
			return err
		}
		if flags.Write {
			ioutil.WriteFile(p, data, 0664)
		} else {
			fmt.Println(string(data))
		}
	}
	return nil
}

// Reads all file information into a buffer, then creates a token set and parses the file, returning a *ast.File
func ParseFile(path string) (*ast.File, []byte, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, nil, err
	}
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "etc.go", string(data), 0)
	return f, data, err
}

// Visits all nodes in the *ast.File (recursively), performing mutations on the buffer when the type found is an *ast.StructType
func Inspect(f *ast.File, srcFileData []byte) ([]byte, error) {
	data := srcFileData
	var offset *int
	offsetVal := 0
	offset = &offsetVal
	ast.Inspect(f, func(n ast.Node) bool {
		switch t := n.(type) {
		case *ast.Ident:
			if t.Obj != nil {
				if t.Obj.Kind == ast.Typ {
					lastTypeName = t.Obj.Name
				}
			}
		case *ast.StructType:
			data = TagStruct(data, t, offset)
		}
		return true
	})
	return format.Source(data)
}

// Tags a struct based on whether or not it is exported, is ignored, and what flags are provided at runtime
func TagStruct(srcData []byte, s *ast.StructType, offset *int) []byte {
	// If the last type name is one of our ignored structs, return immediately
	if IsIgnoredTypeName(lastTypeName) {
		return srcData
	}
	for _, f := range s.Fields.List {
		if len(f.Names) == 0 {
			fmt.Printf("Could not find name for field: %+v\n", f)
			continue
		}
		if f.Names[0].IsExported() {
			name := f.Names[0].Name
			var formattedName string
			if IsIgnoredField(name) {
				formattedName = "-"
			} else {
				formattedName = FormatFieldName(name)
			}
			tag := f.Tag
			if tag != nil {
				val := tag.Value
				// remove `'s from string and convert to a reflect.StructTag so we can use reflect.StructTag().Get() call
				reflectTag := reflect.StructTag(val[1 : len(val)-1])
				if !flags.Overwrite {
					currentTagValue := reflectTag.Get(flags.Tag)
					if currentTagValue != "" {
						sterrors.Printf("Existing tag found: TagName: %s, TagValue: %s, StartIndex: %d, EndIndex: %d - Skipping Tag\n", flags.Tag, currentTagValue, tag.Pos(), tag.End())
						continue
					}
				}
				srcData = OverwriteStructTag(tag, formattedName, offset, srcData)
			} else {
				srcData = AddStructTag(f, formattedName, offset, srcData)
			}
		}

	}
	return srcData
}

// Adds an additional tag to a struct tag
func AddStructTag(field *ast.Field, tagName string, offset *int, data []byte) []byte {
	start := int(field.End()) + *offset - 1
	tag := fmt.Sprintf(" `%s:\"%s\"`", flags.Tag, tagName)
	*offset += len(tag)
	return Insert(data, []byte(tag), start)
}

// Overwrites the struct tag completely
func OverwriteStructTag(tag *ast.BasicLit, tagName string, offset *int, data []byte) []byte {
	val := tag.Value
	start := int(tag.Pos()) + *offset - 1
	end := int(tag.End()) + *offset - 1
	length := len(val)
	oldLength := end - start

	// Delete the original tag
	data = DeleteRange(data, start, end)
	var newTag string
	if flags.Append {
		oldTag := removeIndex(removeIndex(val, 0), len(val)-2)
		newTag = fmt.Sprintf("`%s:\"%s\" %s`", flags.Tag, tagName, oldTag)
	} else {
		newTag = fmt.Sprintf("`%s:\"%s\"`", flags.Tag, tagName)
	}

	numSpaces := len(newTag) - oldLength - 1
	var spaces string

	// Can't pass a negative number to strings.Repeat()
	// it will cause a panic because it passes this number directly to make()
	if numSpaces > 0 {
		spaces = strings.Repeat(" ", numSpaces)
	}

	newTag = fmt.Sprintf("%s%s", spaces, newTag)
	localOffset := len(newTag) - length
	*offset += localOffset

	// Insert new tag
	data = Insert(data, []byte(newTag), start)
	return data
}

// Checks if a fiels is an explicitly ignored field
func IsIgnoredField(s string) bool {
	for _, f := range flags.IgnoredFields {
		if s == f {
			return true
		}
	}
	return false
}

func IsIgnoredTypeName(s string) bool {
	for _, n := range flags.IgnoredStructs {
		if n == s {
			return true
		}
	}
	return false
}

// Deletes a range from a slice, returning the new slice
func DeleteRange(data []byte, start, end int) []byte {
	return append(data[:start], data[end:]...)
}

// Inserts []byte at the given start index
func Insert(data, insertData []byte, start int) []byte {
	return append(data[:start], append(insertData, data[start:]...)...)
}

// Removes a single index from a string
func removeIndex(input string, index int) string {
	return input[:index] + input[index+1:]
}

// Formats the field name as either CamelCase or snake_case
func FormatFieldName(n string) string {
	switch flags.Case {
	case flags.Camel:
		return CamelCase(n)
	case flags.Snake:
		return Underscore(n)
	}
	sterrors.Printf("Could not format string, Case is not set.\n")
	return n
}

var (
	doubleColon       = regexp.MustCompile("::")
	dash              = regexp.MustCompile("-")
	uppersOrNumsLower = regexp.MustCompile("([A-Z0-9]+)([A-Z][a-z])")
	lowerUpper        = regexp.MustCompile("([a-z])([A-Z0-9])")
)

// This function will change a string from a camelcased
// form to a string with underscores. Will change "::" to
// "/" to maintain compatibility with Rails's underscore
func Underscore(str string) string {
	output := doubleColon.ReplaceAllString(str, "/")

	// Rails uses underscores while I use dashes in this function
	// Go's regexp doesn't like $1_$2, so we'll use a dash instead
	// since it will get fixed in a later replacement
	output = uppersOrNumsLower.ReplaceAllString(output, "$1-$2")
	output = lowerUpper.ReplaceAllString(output, "$1-$2")

	output = strings.ToLower(output)
	output = dash.ReplaceAllString(output, "_")

	return output
}

var camelingRegex = regexp.MustCompile("[0-9A-Za-z]+")

// Converts a string to the CamelCase version of it
func CamelCase(src string) string {
	byteSrc := []byte(src)
	chunks := camelingRegex.FindAll(byteSrc, -1)
	for idx, val := range chunks {
		if idx > 0 {
			chunks[idx] = bytes.Title(val)
		}
	}
	return string(bytes.Join(chunks, nil))
}
<commit_msg>fix misspelling<commit_after>// Most functions in package parse are exported on the off chance that someone would like to use them as library functions
// in their own project
package parse

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"regexp"
	"strings"

	"reflect"

	"flag"

	"go/format"

	"github.com/alistanis/st/flags"
	"github.com/alistanis/st/sterrors"
)

var (
	lastTypeName string
)

// Takes all provided arguments, iterates over them, stats them, and then inspects source files
func ParseAndProcess() error {
	for _, p := range flag.Args() {
		fi, err := os.Stat(p)
		if err != nil {
			return err
		}
		if fi.IsDir() {
			return fmt.Errorf("Cannot use a directory as a path. Path: %s", fi.Name())
		}
		f, data, err := ParseFile(p)
		if err != nil {
			return err
		}
		data, err = Inspect(f, data)
		if err != nil {
			return err
		}
		if flags.Write {
			ioutil.WriteFile(p, data, 0664)
		} else {
			fmt.Println(string(data))
		}
	}
	return nil
}

// Reads all file information into a buffer, then creates a token set and parses the file, returning a *ast.File
func ParseFile(path string) (*ast.File, []byte, error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, nil, err
	}
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "etc.go", string(data), 0)
	return f, data, err
}

// Visits all nodes in the *ast.File (recursively), performing mutations on the buffer when the type found is an *ast.StructType
func Inspect(f *ast.File, srcFileData []byte) ([]byte, error) {
	data := srcFileData
	var offset *int
	offsetVal := 0
	offset = &offsetVal
	ast.Inspect(f, func(n ast.Node) bool {
		switch t := n.(type) {
		case *ast.Ident:
			if t.Obj != nil {
				if t.Obj.Kind == ast.Typ {
					lastTypeName = t.Obj.Name
				}
			}
		case *ast.StructType:
			data = TagStruct(data, t, offset)
		}
		return true
	})
	return format.Source(data)
}

// Tags a struct based on whether or not it is exported, is ignored, and what flags are provided at runtime
func TagStruct(srcData []byte, s *ast.StructType, offset *int) []byte {
	// If the last type name is one of our ignored structs, return immediately
	if IsIgnoredTypeName(lastTypeName) {
		return srcData
	}
	for _, f := range s.Fields.List {
		if len(f.Names) == 0 {
			fmt.Printf("Could not find name for field: %+v\n", f)
			continue
		}
		if f.Names[0].IsExported() {
			name := f.Names[0].Name
			var formattedName string
			if IsIgnoredField(name) {
				formattedName = "-"
			} else {
				formattedName = FormatFieldName(name)
			}
			tag := f.Tag
			if tag != nil {
				val := tag.Value
				// remove `'s from string and convert to a reflect.StructTag so we can use reflect.StructTag().Get() call
				reflectTag := reflect.StructTag(val[1 : len(val)-1])
				if !flags.Overwrite {
					currentTagValue := reflectTag.Get(flags.Tag)
					if currentTagValue != "" {
						sterrors.Printf("Existing tag found: TagName: %s, TagValue: %s, StartIndex: %d, EndIndex: %d - Skipping Tag\n", flags.Tag, currentTagValue, tag.Pos(), tag.End())
						continue
					}
				}
				srcData = OverwriteStructTag(tag, formattedName, offset, srcData)
			} else {
				srcData = AddStructTag(f, formattedName, offset, srcData)
			}
		}

	}
	return srcData
}

// Adds an additional tag to a struct tag
func AddStructTag(field *ast.Field, tagName string, offset *int, data []byte) []byte {
	start := int(field.End()) + *offset - 1
	tag := fmt.Sprintf(" `%s:\"%s\"`", flags.Tag, tagName)
	*offset += len(tag)
	return Insert(data, []byte(tag), start)
}

// Overwrites the struct tag completely
func OverwriteStructTag(tag *ast.BasicLit, tagName string, offset *int, data []byte) []byte {
	val := tag.Value
	start := int(tag.Pos()) + *offset - 1
	end := int(tag.End()) + *offset - 1
	length := len(val)
	oldLength := end - start

	// Delete the original tag
	data = DeleteRange(data, start, end)
	var newTag string
	if flags.Append {
		oldTag := removeIndex(removeIndex(val, 0), len(val)-2)
		newTag = fmt.Sprintf("`%s:\"%s\" %s`", flags.Tag, tagName, oldTag)
	} else {
		newTag = fmt.Sprintf("`%s:\"%s\"`", flags.Tag, tagName)
	}

	numSpaces := len(newTag) - oldLength - 1
	var spaces string

	// Can't pass a negative number to strings.Repeat()
	// it will cause a panic because it passes this number directly to make()
	if numSpaces > 0 {
		spaces = strings.Repeat(" ", numSpaces)
	}

	newTag = fmt.Sprintf("%s%s", spaces, newTag)
	localOffset := len(newTag) - length
	*offset += localOffset

	// Insert new tag
	data = Insert(data, []byte(newTag), start)
	return data
}

// Checks if a fiels is an explicitly ignored field
func IsIgnoredField(s string) bool {
	for _, f := range flags.IgnoredFields {
		if s == f {
			return true
		}
	}
	return false
}

func IsIgnoredTypeName(s string) bool {
	for _, n := range flags.IgnoredStructs {
		if n == s {
			return true
		}
	}
	return false
}

// Deletes a range from a slice, returning the new slice
func DeleteRange(data []byte, start, end int) []byte {
	return append(data[:start], data[end:]...)
}

// Inserts []byte at the given start index
func Insert(data, insertData []byte, start int) []byte {
	return append(data[:start], append(insertData, data[start:]...)...)
}

// Removes a single index from a string
func removeIndex(input string, index int) string {
	return input[:index] + input[index+1:]
}

// Formats the field name as either CamelCase or snake_case
func FormatFieldName(n string) string {
	switch flags.Case {
	case flags.Camel:
		return CamelCase(n)
	case flags.Snake:
		return Underscore(n)
	}
	sterrors.Printf("Could not format string, Case is not set.\n")
	return n
}

var (
	doubleColon       = regexp.MustCompile("::")
	dash              = regexp.MustCompile("-")
	uppersOrNumsLower = regexp.MustCompile("([A-Z0-9]+)([A-Z][a-z])")
	lowerUpper        = regexp.MustCompile("([a-z])([A-Z0-9])")
)

// This function will change a string from a camelcased
// form to a string with underscores. Will change "::" to
// "/" to maintain compatibility with Rails's underscore
func Underscore(str string) string {
	output := doubleColon.ReplaceAllString(str, "/")

	// Rails uses underscores while I use dashes in this function
	// Go's regexp doesn't like $1_$2, so we'll use a dash instead
	// since it will get fixed in a later replacement
	output = uppersOrNumsLower.ReplaceAllString(output, "$1-$2")
	output = lowerUpper.ReplaceAllString(output, "$1-$2")

	output = strings.ToLower(output)
	output = dash.ReplaceAllString(output, "_")

	return output
}

var camelingRegex = regexp.MustCompile("[0-9A-Za-z]+")

// Converts a string to the CamelCase version of it
func CamelCase(src string) string {
	byteSrc := []byte(src)
	chunks := camelingRegex.FindAll(byteSrc, -1)
	for idx, val := range chunks {
		if idx > 0 {
			chunks[idx] = bytes.Title(val)
		}
	}
	return string(bytes.Join(chunks, nil))
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, Rob Thornton
// All rights reserved.
// This source code is governed by a Simplied BSD-License. Please see the
// LICENSE included in this distribution for a copy of the full license
// or, if one is not included, you may also find a copy at
// http://opensource.org/licenses/BSD-2-Clause

package parse

import (
	"os"

	"github.com/rthornton128/calc/ast"
	"github.com/rthornton128/calc/scan"
	"github.com/rthornton128/calc/token"
)

func ParseFile(filename, src string) *ast.File {
	var p parser
	p.init(filename, src)
	f := p.parseFile()
	if p.errors.Count() > 0 {
		p.errors.Print()
		return nil
	}
	return f
}

type parser struct {
	file    *token.File
	errors  scan.ErrorList
	scanner scan.Scanner

	curScope *ast.Scope
	topScope *ast.Scope

	pos token.Pos
	tok token.Token
	lit string
}

func (p *parser) addError(msg string) {
	p.errors.Add(p.file.Position(p.pos), msg)
	if p.errors.Count() >= 10 {
		p.errors.Print()
		os.Exit(1)
	}
}

func (p *parser) expect(tok token.Token) token.Pos {
	pos := p.pos
	if p.tok != tok {
		p.addError("Expected '" + tok.String() + "' got '" + p.lit + "'")
	}
	p.next()
	return pos
}

func (p *parser) init(fname, src string) {
	p.file = token.NewFile(fname, 1, len(src))
	p.scanner.Init(p.file, src)
	p.curScope = ast.NewScope(nil)
	p.topScope = p.curScope
	p.next()
}

func (p *parser) next() {
	p.lit, p.tok, p.pos = p.scanner.Scan()
}

func (p *parser) openScope() {
	p.curScope = ast.NewScope(p.curScope)
}

func (p *parser) closeScope() {
	p.curScope = p.curScope.Parent()
}

func (p *parser) parseBasicLit() *ast.BasicLit {
	return &ast.BasicLit{LitPos: p.pos, Kind: p.tok, Lit: p.lit}
}

func (p *parser) parseBinaryExpr(open token.Pos) *ast.BinaryExpr {
	pos := p.pos
	op := p.tok
	p.next()

	var list []ast.Expr
	for p.tok != token.RPAREN && p.tok != token.EOF {
		list = append(list, p.parseGenExpr())
	}
	if len(list) < 2 {
		p.addError("binary expression must have at least two operands")
	}
	end := p.expect(token.RPAREN)
	return &ast.BinaryExpr{
		Expression: ast.Expression{
			Opening: open,
			Closing: end,
		},
		Op:    op,
		OpPos: pos,
		List:  list,
	}
}

func (p *parser) parseGenExpr() ast.Expr {
	var expr ast.Expr

	switch p.tok {
	case token.LPAREN:
		expr = p.parseExpr()
	case token.INTEGER:
		expr = p.parseBasicLit()
		p.next()
	default:
		p.addError("Expected '" + token.LPAREN.String() + "' or '" +
			token.INTEGER.String() + "' got '" + p.lit + "'")
		p.next()
	}

	return expr
}

func (p *parser) parseExpr() ast.Expr {
	var expr ast.Expr

	pos := p.expect(token.LPAREN)
	switch p.tok {
	case token.ADD, token.SUB, token.MUL, token.QUO, token.REM,
		token.EQL, token.GTE, token.GTT, token.NEQ, token.LST, token.LTE:
		expr = p.parseBinaryExpr(pos)
	case token.VAR:
		expr = p.parseVarExpr(pos)
	default:
		p.addError("Expected binary operator but got '" + p.lit + "'")
	}

	return expr
}

func (p *parser) parseFile() *ast.File {
	var expr ast.Expr
	expr = p.parseGenExpr()
	if p.tok != token.EOF {
		p.addError("Expected EOF, got '" + p.lit + "'")
	}
	scope := ast.NewScope(nil)
	scope.Insert(&ast.Ident{NamePos: token.NoPos, Name: "main",
		Type: &ast.Ident{NamePos: token.NoPos, Name: "int", Type: nil}},
		expr)
	return &ast.File{Scope: scope}
}

func (p *parser) parseIdent() *ast.Ident {
	pos := p.expect(token.IDENT)
	return &ast.Ident{NamePos: pos, Name: p.lit, Type: nil} // TODO: NewObject?
}

func (p *parser) parseVarExpr(lparen token.Pos) *ast.VarExpr {
	varpos := p.expect(token.VAR)
	nam := p.parseIdent()
	typ := p.parseIdent()
	val := p.parseGenExpr()
	rparen := p.expect(token.RPAREN)

	// p.curScope.Insert(...)

	return &ast.VarExpr{
		Expression: ast.Expression{Opening: lparen, Closing: rparen},
		Var:        varpos,
		Name:       nam,
		Type:       typ,
		Value:      val,
	}
}
<commit_msg>parseBasicLit and parseIdentifier should advance parser<commit_after>// Copyright (c) 2014, Rob Thornton
// All rights reserved.
// This source code is governed by a Simplied BSD-License. Please see the
// LICENSE included in this distribution for a copy of the full license
// or, if one is not included, you may also find a copy at
// http://opensource.org/licenses/BSD-2-Clause

package parse

import (
	"os"

	"github.com/rthornton128/calc/ast"
	"github.com/rthornton128/calc/scan"
	"github.com/rthornton128/calc/token"
)

func ParseFile(filename, src string) *ast.File {
	var p parser
	p.init(filename, src)
	f := p.parseFile()
	if p.errors.Count() > 0 {
		p.errors.Print()
		return nil
	}
	return f
}

type parser struct {
	file    *token.File
	errors  scan.ErrorList
	scanner scan.Scanner

	curScope *ast.Scope
	topScope *ast.Scope

	pos token.Pos
	tok token.Token
	lit string
}

func (p *parser) addError(msg string) {
	p.errors.Add(p.file.Position(p.pos), msg)
	if p.errors.Count() >= 10 {
		p.errors.Print()
		os.Exit(1)
	}
}

func (p *parser) expect(tok token.Token) token.Pos {
	pos := p.pos
	if p.tok != tok {
		p.addError("Expected '" + tok.String() + "' got '" + p.lit + "'")
	}
	p.next()
	return pos
}

func (p *parser) init(fname, src string) {
	p.file = token.NewFile(fname, 1, len(src))
	p.scanner.Init(p.file, src)
	p.curScope = ast.NewScope(nil)
	p.topScope = p.curScope
	p.next()
}

func (p *parser) next() {
	p.lit, p.tok, p.pos = p.scanner.Scan()
}

func (p *parser) openScope() {
	p.curScope = ast.NewScope(p.curScope)
}

func (p *parser) closeScope() {
	p.curScope = p.curScope.Parent()
}

func (p *parser) parseBasicLit() *ast.BasicLit {
	pos, tok, lit := p.pos, p.tok, p.lit
	p.next()
	return &ast.BasicLit{LitPos: pos, Kind: tok, Lit: lit}
}

func (p *parser) parseBinaryExpr(open token.Pos) *ast.BinaryExpr {
	pos := p.pos
	op := p.tok
	p.next()

	var list []ast.Expr
	for p.tok != token.RPAREN && p.tok != token.EOF {
		list = append(list, p.parseGenExpr())
	}
	if len(list) < 2 {
		p.addError("binary expression must have at least two operands")
	}
	end := p.expect(token.RPAREN)
	return &ast.BinaryExpr{
		Expression: ast.Expression{
			Opening: open,
			Closing: end,
		},
		Op:    op,
		OpPos: pos,
		List:  list,
	}
}

func (p *parser) parseGenExpr() ast.Expr {
	var expr ast.Expr

	switch p.tok {
	case token.LPAREN:
		expr = p.parseExpr()
	case token.INTEGER:
		expr = p.parseBasicLit()
	default:
		p.addError("Expected '" + token.LPAREN.String() + "' or '" +
			token.INTEGER.String() + "' got '" + p.lit + "'")
		p.next()
	}

	return expr
}

func (p *parser) parseExpr() ast.Expr {
	var expr ast.Expr

	pos := p.expect(token.LPAREN)
	switch p.tok {
	case token.ADD, token.SUB, token.MUL, token.QUO, token.REM,
		token.EQL, token.GTE, token.GTT, token.NEQ, token.LST, token.LTE:
		expr = p.parseBinaryExpr(pos)
	case token.VAR:
		expr = p.parseVarExpr(pos)
	default:
		p.addError("Expected binary operator but got '" + p.lit + "'")
	}

	return expr
}

func (p *parser) parseFile() *ast.File {
	var expr ast.Expr
	expr = p.parseGenExpr()
	if p.tok != token.EOF {
		p.addError("Expected EOF, got '" + p.lit + "'")
	}
	scope := ast.NewScope(nil)
	scope.Insert(&ast.Ident{NamePos: token.NoPos, Name: "main",
		Type: &ast.Ident{NamePos: token.NoPos, Name: "int", Type: nil}},
		expr)
	return &ast.File{Scope: scope}
}

func (p *parser) parseIdent() *ast.Ident {
	pos := p.expect(token.IDENT)
	name := p.lit
	p.next()
	return &ast.Ident{NamePos: pos, Name: name}
}

func (p *parser) parseVarExpr(lparen token.Pos) *ast.VarExpr {
	varpos := p.expect(token.VAR)
	nam := p.parseIdent()
	typ := p.parseIdent()
	val := p.parseGenExpr()
	rparen := p.expect(token.RPAREN)

	// p.curScope.Insert(...)

	return &ast.VarExpr{
		Expression: ast.Expression{Opening: lparen, Closing: rparen},
		Var:        varpos,
		Name:       nam,
		Type:       typ,
		Value:      val,
	}
}
<|endoftext|>


<commit_before>package parser

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
)

// Checks if a slice of strings contains a given string
func stringInSlice(s string, list []string) bool {
	for _, v := range list {
		if v == s {
			return true
		}
	}
	return false
}

// Exit with a nicely colored error message
func exitWithError(s string) {
	io.WriteString(os.Stderr, fail(s)+"\n")
	os.Exit(1)
}

// Formats input as pretty JSON
func formatJSON(text []byte) string {
	var prettyJSON bytes.Buffer
	err := json.Indent(&prettyJSON, text, "", "\t")
	if err != nil {
		log.Fatal("JSON parse error: ", err)
	}
	return fmt.Sprintf("\n%s", prettyJSON.String())
}
<commit_msg>Nothing important<commit_after>package parser

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
)

// Checks if a slice of strings contains a given string
func stringInSlice(s string, list []string) bool {
	for _, v := range list {
		if v == s {
			return true
		}
	}
	return false
}

// Formats input as pretty JSON
func formatJSON(text []byte) string {
	var prettyJSON bytes.Buffer
	err := json.Indent(&prettyJSON, text, "", "\t")
	if err != nil {
		log.Fatal("JSON parse error: ", err)
	}
	return fmt.Sprintf("\n%s", prettyJSON.String())
}

// Exit with a nicely colored error message
func exitWithError(s string) {
	io.WriteString(os.Stderr, fail(s)+"\n")
	os.Exit(1)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/gotk3/gotk3/cairo"
	"github.com/gotk3/gotk3/gdk"
	"github.com/gotk3/gotk3/gtk"
	"log"
//	"math/cmplx"
	"fmt"
	"math"
	"time"
	"sync"
	"runtime"
	"reflect"
	"strings"
)

type ma struct {
	Iter int `dl:"%d"`

	LastDuration time.Duration `dl:"%v,time"`

	dl dataLabels
}

func newma() *ma {
	ma := &ma{}
	return ma
}

var palette = [...][3]float64{
	{ 1.00, 0.00, 0.00 },
	{ 1.00, 1.00, 0.00 },
	{ 0.00, 1.00, 1.00 },
}

var log_escape = math.Log(2)

func getColor(abs float64, i int) (byte, byte, byte) {
	mu := float64(i + 1) - math.Log(math.Log(abs)) / log_escape
	mu /= 16
	clr1 := int(mu)

	t2 := mu - float64(clr1)
	t1 := 1.0 - t2

	c1 := palette[clr1 % len(palette)]
	c2 := palette[(clr1 + 1) % len(palette)]

	return byte(255 * (c1[0] * t1 + c2[0] * t2)),
		byte(255 * (c1[1] * t1 + c2[1] * t2)),
		byte(255 * (c1[2] * t1 + c2[2] * t2))
}

func (ma *ma)redraw(cx, cy, zw float64, pb *gdk.Pixbuf) {
	w := pb.GetWidth()
	h := pb.GetHeight()
	nc := pb.GetNChannels()
	rs := pb.GetRowstride()
	px := pb.GetPixels()

	aspect := float64(h) / float64(w)

	sx := zw / float64(w - 1)
	sy := zw * aspect / float64(h - 1)

	// http://math.stackexchange.com/a/30560
	ma.Iter = int(math.Sqrt(math.Abs(2.0 * math.Sqrt(math.Abs(1 - math.Sqrt(5.0 / zw))))) * 66.5)

	startt := time.Now()

	var wg sync.WaitGroup

	steps := runtime.NumCPU()
	for i := 0; i < steps; i++ {
		wg.Add(1)
		go func(starty, endy int) {
			for y := starty; y < endy; y++ {
				ci := cy - (zw * aspect / 2) + float64(y) * sy
				for x := 0; x < w; x++ {
					cr := cx - (zw / 2) + float64(x) * sx
					o := y * rs + x * nc

					c := complex(cr, ci)
					z := c
					px[o], px[o + 1], px[o +2] = 0, 0, 0
					for i := 0; i < ma.Iter; i++ {
						re, im := real(z), imag(z)
						l := re * re + im * im
						if l > 4.0 {
							px[o], px[o + 1], px[o + 2] = getColor(l, i)
							break
						}
						z = z * z + c
					}
				}
			}
			wg.Done()
		}(i * h / steps, (i + 1) * h / steps)
	}

	wg.Wait()
	ma.LastDuration = time.Since(startt)
}

type datalabel struct {
	name string
	fmt string
	keyLabel *gtk.Label
	valLabel *gtk.Label
}

type dataLabels struct {
	labels []datalabel
}

func (dl *dataLabels)populate(src interface{}, gr *gtk.Grid) {
	l := func(s string) *gtk.Label {
		label, err := gtk.LabelNew(s)
		if err != nil {
			log.Fatal(err)
		}
		label.SetWidthChars(10)
		return label
	}

	srcv := reflect.ValueOf(src)
	srct := srcv.Type()

	for i := 0; i < srct.NumField(); i++ {
		ft := srct.Field(i)
		tags := strings.SplitN(ft.Tag.Get("dl"), ",", 2)
		if tags[0] == "" {
			continue
		}

		ln := ft.Name
		if len(tags) == 2 {
			ln = tags[1]
		}
		dl.labels = append(dl.labels, datalabel{ fmt: tags[0], name: ft.Name, keyLabel: l(ln), valLabel: l("") })
	}

	for i := range dl.labels {
		gr.Attach(dl.labels[i].keyLabel, 0, i, 1, 1)
		gr.Attach(dl.labels[i].valLabel, 1, i, 1, 1)
	}
}

func (dl dataLabels)update(obj interface{}) {
	v := reflect.ValueOf(obj)

	for _, l := range dl.labels {
		l.valLabel.SetText(fmt.Sprintf(l.fmt, v.FieldByName(l.name).Interface()))
	}
}

const build = `
<interface>
  <object class="GtkBox" id="everything">
    <property name="orientation">horizontal</property>
    <child>
      <object class="GtkEventBox" id="eb">
        <child>
          <object class="GtkDrawingArea">
            <signal name="draw" handler="drawArea" />
            <signal name="size-allocate" handler="resize" />
            <property name="width-request">256</property>
            <property name="height-request">256</property>
          </object>
        </child>
        <property name="events">GDK_SCROLL_MASK</property>
        <signal name="button_press_event" handler="moveTo" />
        <signal name="scroll-event" handler="zoomTo" />
      </object>
      <packing>
        <property name="expand">true</property>
      </packing>
    </child>

   <child>
     <object class="GtkGrid" id="labels">
     </object>
   </child>

  </object>
</interface>
`

func (ma *ma)buildWidgets() gtk.IWidget {
	builder, err := gtk.BuilderNew()
	if err != nil {
		log.Fatal(err)
	}
	err = builder.AddFromString(build)
	if err != nil {
		log.Fatal(err)
	}

	ebi, err := builder.GetObject("eb")
	if err != nil {
		log.Fatal(err)
	}
	eb := ebi.(*gtk.EventBox)

	// XXX - how do we set this property in the xml?
	eb.AddEvents(int(gdk.SCROLL_MASK))

	zw := 3.0
	cx := -0.5
	cy := 0.0

	var pb *gdk.Pixbuf

	allocpb := func(nw, nh int) {
		s := nw
		if s > nh {
			s = nh
		}
		pb, err = gdk.PixbufNew(gdk.COLORSPACE_RGB, false, 8, s, s)
		if err != nil {
			log.Fatal("pixbuf: ", err)
		}
	}
	allocpb(256, 256)

	builder.ConnectSignals(map[string]interface{}{
		"resize": func(da *gtk.DrawingArea, p uintptr) {
			rect := gdk.WrapRectangle(p)
			allocpb(rect.GetWidth(), rect.GetHeight())
		},
		"drawArea": func(da *gtk.DrawingArea, cr *cairo.Context) {
			ma.redraw(cx, cy, zw, pb)
			ma.dl.update(*ma)
			gtk.GdkCairoSetSourcePixBuf(cr, pb, 0, 0)
			cr.Paint()
		},
		"moveTo": func(win *gtk.Window, ev *gdk.Event) {
			e := &gdk.EventButton{ev}
			cx = cx - (zw / 2) + e.X() * zw / float64(pb.GetWidth() - 1)
			cy = cy - (zw / 2) + e.Y() * zw / float64(pb.GetHeight() - 1)		// assumes square pb (should be w?)
			eb.QueueDraw()
		},
		"zoomTo": func(win *gtk.Window, ev *gdk.Event) {
			e := &gdk.EventScroll{ev}
			delta := e.DeltaY()
			if delta > 0.5 {
				delta = 0.5
			}
			delta *= (zw / 5.0)

			switch e.Direction() {
			case gdk.SCROLL_UP:
				delta = -delta
			case gdk.SCROLL_DOWN:
				// nothing
			default:
				delta = 0
			}

			// We want the screen to canvas translated coordinate be the same before and after the zoom.
			cx += delta * (0.5 - e.X() / float64(pb.GetWidth() - 1))
			cy += delta * (0.5 - e.Y() / float64(pb.GetHeight() - 1)) // wrong - works only on square pb.
			zw += delta
			eb.QueueDraw()
		},
	})

	gri, err := builder.GetObject("labels")
	if err != nil {
		log.Fatal(err)
	}
	gr := gri.(*gtk.Grid)

	ma.dl.populate(*ma, gr)

	obj, err := builder.GetObject("everything")
	if err != nil {
		log.Fatal(err)
	}

	return obj.(gtk.IWidget)
}

func main() {
	runtime.LockOSThread()
	gtk.Init(nil)
	win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
	if err != nil {
		log.Fatal(err)
	}
	win.Connect("destroy", gtk.MainQuit)

	ma := newma()

	win.Add(ma.buildWidgets())
	win.ShowAll()

	gtk.Main()
}
<commit_msg>More reorganization.<commit_after>package main

import (
	"github.com/gotk3/gotk3/cairo"
	"github.com/gotk3/gotk3/gdk"
	"github.com/gotk3/gotk3/gtk"
	"log"
//	"math/cmplx"
	"fmt"
	"math"
	"time"
	"sync"
	"runtime"
	"reflect"
	"strings"
)

type ma struct {
	Iter int `dl:"%d"`
	LastDuration time.Duration `dl:"%v,time"`

	dl dataLabels
}

func newma() *ma {
	ma := &ma{}
	return ma
}

var palette = [...][3]float64{
	{ 1.00, 0.00, 0.00 },
	{ 1.00, 1.00, 0.00 },
	{ 0.00, 1.00, 1.00 },
}

var log_escape = math.Log(2)

func getColor(abs float64, i int) (byte, byte, byte) {
	mu := float64(i + 1) - math.Log(math.Log(abs)) / log_escape
	mu /= 16
	clr1 := int(mu)

	t2 := mu - float64(clr1)
	t1 := 1.0 - t2

	c1 := palette[clr1 % len(palette)]
	c2 := palette[(clr1 + 1) % len(palette)]

	return byte(255 * (c1[0] * t1 + c2[0] * t2)),
		byte(255 * (c1[1] * t1 + c2[1] * t2)),
		byte(255 * (c1[2] * t1 + c2[2] * t2))
}

func (ma *ma)Redraw(cx, cy, zw float64, pb *gdk.Pixbuf) {
	w := pb.GetWidth()
	h := pb.GetHeight()
	nc := pb.GetNChannels()
	rs := pb.GetRowstride()
	px := pb.GetPixels()

	aspect := float64(h) / float64(w)

	sx := zw / float64(w - 1)
	sy := zw * aspect / float64(h - 1)

	// http://math.stackexchange.com/a/30560
	ma.Iter = int(math.Sqrt(math.Abs(2.0 * math.Sqrt(math.Abs(1 - math.Sqrt(5.0 / zw))))) * 66.5)

	startt := time.Now()

	var wg sync.WaitGroup

	steps := runtime.NumCPU()
	for i := 0; i < steps; i++ {
		wg.Add(1)
		go func(starty, endy int) {
			for y := starty; y < endy; y++ {
				ci := cy - (zw * aspect / 2) + float64(y) * sy
				for x := 0; x < w; x++ {
					cr := cx - (zw / 2) + float64(x) * sx
					o := y * rs + x * nc

					c := complex(cr, ci)
					z := c
					px[o], px[o + 1], px[o +2] = 0, 0, 0
					for i := 0; i < ma.Iter; i++ {
						re, im := real(z), imag(z)
						l := re * re + im * im
						if l > 4.0 {
							px[o], px[o + 1], px[o + 2] = getColor(l, i)
							break
						}
						z = z * z + c
					}
				}
			}
			wg.Done()
		}(i * h / steps, (i + 1) * h / steps)
	}

	wg.Wait()
	ma.LastDuration = time.Since(startt)
	log.Print(ma.LastDuration)
	ma.dl.update(*ma)
}

type datalabel struct {
	name string
	fmt string
	keyLabel *gtk.Label
	valLabel *gtk.Label
}

type dataLabels struct {
	labels []datalabel
}

func (dl *dataLabels)populate(src interface{}, gr *gtk.Grid) {
	l := func(s string) *gtk.Label {
		label, err := gtk.LabelNew(s)
		if err != nil {
			log.Fatal(err)
		}
		label.SetWidthChars(10)
		return label
	}

	srcv := reflect.ValueOf(src)
	srct := srcv.Type()

	for i := 0; i < srct.NumField(); i++ {
		ft := srct.Field(i)
		tags := strings.SplitN(ft.Tag.Get("dl"), ",", 2)
		if tags[0] == "" {
			continue
		}

		ln := ft.Name
		if len(tags) == 2 {
			ln = tags[1]
		}
		dl.labels = append(dl.labels, datalabel{ fmt: tags[0], name: ft.Name, keyLabel: l(ln), valLabel: l("") })
	}

	for i := range dl.labels {
		gr.Attach(dl.labels[i].keyLabel, 0, i, 1, 1)
		gr.Attach(dl.labels[i].valLabel, 1, i, 1, 1)
	}
}

func (dl dataLabels)update(obj interface{}) {
	v := reflect.ValueOf(obj)

	for _, l := range dl.labels {
		if l.valLabel != nil {
			l.valLabel.SetText(fmt.Sprintf(l.fmt, v.FieldByName(l.name).Interface()))
		}
	}
}

const build = `
<interface>
  <object class="GtkBox" id="everything">
    <property name="orientation">horizontal</property>
    <child>
      <object class="GtkEventBox" id="eb">
        <child>
          <object class="GtkDrawingArea">
            <signal name="draw" handler="drawArea" />
            <signal name="size-allocate" handler="resize" />
            <property name="width-request">256</property>
            <property name="height-request">256</property>
          </object>
        </child>
        <property name="events">GDK_SCROLL_MASK</property>
        <signal name="button_press_event" handler="moveTo" />
        <signal name="scroll-event" handler="zoomTo" />
      </object>
      <packing>
        <property name="expand">true</property>
      </packing>
    </child>

   <child>
     <object class="GtkGrid" id="labels">
     </object>
   </child>

  </object>
</interface>
`

func buildWidgets() gtk.IWidget {
	builder, err := gtk.BuilderNew()
	if err != nil {
		log.Fatal(err)
	}
	err = builder.AddFromString(build)
	if err != nil {
		log.Fatal(err)
	}

	ebi, err := builder.GetObject("eb")
	if err != nil {
		log.Fatal(err)
	}
	eb := ebi.(*gtk.EventBox)

	// XXX - how do we set this property in the xml?
	eb.AddEvents(int(gdk.SCROLL_MASK))

	zw := 3.0
	cx := -0.5
	cy := 0.0

	var pb *gdk.Pixbuf

	allocpb := func(nw, nh int) {
		s := nw
		if s > nh {
			s = nh
		}
		pb, err = gdk.PixbufNew(gdk.COLORSPACE_RGB, false, 8, s, s)
		if err != nil {
			log.Fatal("pixbuf: ", err)
		}
	}
	allocpb(256, 256)

	ma := newma()

	builder.ConnectSignals(map[string]interface{}{
		"resize": func(da *gtk.DrawingArea, p uintptr) {
			rect := gdk.WrapRectangle(p)
			allocpb(rect.GetWidth(), rect.GetHeight())
		},
		"drawArea": func(da *gtk.DrawingArea, cr *cairo.Context) {
			ma.Redraw(cx, cy, zw, pb)
			gtk.GdkCairoSetSourcePixBuf(cr, pb, 0, 0)
			cr.Paint()
		},
		"moveTo": func(win *gtk.Window, ev *gdk.Event) {
			e := &gdk.EventButton{ev}
			cx = cx - (zw / 2) + e.X() * zw / float64(pb.GetWidth() - 1)
			cy = cy - (zw / 2) + e.Y() * zw / float64(pb.GetHeight() - 1)		// assumes square pb (should be w?)
			eb.QueueDraw()
		},
		"zoomTo": func(win *gtk.Window, ev *gdk.Event) {
			e := &gdk.EventScroll{ev}
			delta := e.DeltaY()
			if delta > 0.5 {
				delta = 0.5
			}
			delta *= (zw / 5.0)

			switch e.Direction() {
			case gdk.SCROLL_UP:
				delta = -delta
			case gdk.SCROLL_DOWN:
				// nothing
			default:
				delta = 0
			}

			// We want the screen to canvas translated coordinate be the same before and after the zoom.
			cx += delta * (0.5 - e.X() / float64(pb.GetWidth() - 1))
			cy += delta * (0.5 - e.Y() / float64(pb.GetHeight() - 1)) // wrong - works only on square pb.
			zw += delta
			eb.QueueDraw()
		},
	})

	gri, err := builder.GetObject("labels")
	if err != nil {
		log.Fatal(err)
	}
	gr := gri.(*gtk.Grid)

	ma.dl.populate(*ma, gr)

	obj, err := builder.GetObject("everything")
	if err != nil {
		log.Fatal(err)
	}

	return obj.(gtk.IWidget)
}

func main() {
	runtime.LockOSThread()
	gtk.Init(nil)
	win, err := gtk.WindowNew(gtk.WINDOW_TOPLEVEL)
	if err != nil {
		log.Fatal(err)
	}
	win.Connect("destroy", gtk.MainQuit)

	win.Add(buildWidgets())
	win.ShowAll()

	gtk.Main()
}
<|endoftext|>


<commit_before>package main

import "github.com/gin-gonic/gin"

func main() {
	dbConn()
	ws()
	dbClose()
}

func ws() {
	r := gin.Default()
	r.LoadHTMLGlob("webroot/templates/*.html")
	r.Static("/static", "webroot/static")
	r.GET("/", index)
	r.GET("/ws", servews)
	r.Run(":80")
}

func index(c *gin.Context) {

	dbSet("ksel", "xuexue")
	title := "testing"
	firstget := dbGet("ksel")
	c.HTML(200, "index.html", gin.H{
		"title": title,
		"redis": firstget,
	})

}

func servews(c *gin.Context) {
	wshandler(c.Writer, c.Request)
}
<commit_msg>change port<commit_after>package main

import "github.com/gin-gonic/gin"

func main() {
	dbConn()
	ws()
	dbClose()
}

func ws() {
	r := gin.Default()
	r.LoadHTMLGlob("webroot/templates/*.html")
	r.Static("/static", "webroot/static")
	r.GET("/", index)
	r.GET("/ws", servews)
	r.Run()
}

func index(c *gin.Context) {

	dbSet("ksel", "xuexue")
	title := "testing"
	firstget := dbGet("ksel")
	c.HTML(200, "index.html", gin.H{
		"title": title,
		"redis": firstget,
	})

}

func servews(c *gin.Context) {
	wshandler(c.Writer, c.Request)
}
<|endoftext|>


<commit_before>package pgtype

import (
	"database/sql/driver"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/jackc/pgx/v5/internal/pgio"
)

type DateScanner interface {
	ScanDate(v Date) error
}

type DateValuer interface {
	DateValue() (Date, error)
}

type Date struct {
	Time             time.Time
	InfinityModifier InfinityModifier
	Valid            bool
}

func (d *Date) ScanDate(v Date) error {
	*d = v
	return nil
}

func (d Date) DateValue() (Date, error) {
	return d, nil
}

const (
	negativeInfinityDayOffset = -2147483648
	infinityDayOffset         = 2147483647
)

// Scan implements the database/sql Scanner interface.
func (dst *Date) Scan(src any) error {
	if src == nil {
		*dst = Date{}
		return nil
	}

	switch src := src.(type) {
	case string:
		return scanPlanTextAnyToDateScanner{}.Scan([]byte(src), dst)
	case time.Time:
		*dst = Date{Time: src, Valid: true}
		return nil
	}

	return fmt.Errorf("cannot scan %T", src)
}

// Value implements the database/sql/driver Valuer interface.
func (src Date) Value() (driver.Value, error) {
	if !src.Valid {
		return nil, nil
	}

	if src.InfinityModifier != Finite {
		return src.InfinityModifier.String(), nil
	}
	return src.Time, nil
}

func (src Date) MarshalJSON() ([]byte, error) {
	if !src.Valid {
		return []byte("null"), nil
	}

	var s string

	switch src.InfinityModifier {
	case Finite:
		s = src.Time.Format("2006-01-02")
	case Infinity:
		s = "infinity"
	case NegativeInfinity:
		s = "-infinity"
	}

	return json.Marshal(s)
}

func (dst *Date) UnmarshalJSON(b []byte) error {
	var s *string
	err := json.Unmarshal(b, &s)
	if err != nil {
		return err
	}

	if s == nil {
		*dst = Date{}
		return nil
	}

	switch *s {
	case "infinity":
		*dst = Date{Valid: true, InfinityModifier: Infinity}
	case "-infinity":
		*dst = Date{Valid: true, InfinityModifier: -Infinity}
	default:
		t, err := time.ParseInLocation("2006-01-02", *s, time.UTC)
		if err != nil {
			return err
		}

		*dst = Date{Time: t, Valid: true}
	}

	return nil
}

type DateCodec struct{}

func (DateCodec) FormatSupported(format int16) bool {
	return format == TextFormatCode || format == BinaryFormatCode
}

func (DateCodec) PreferredFormat() int16 {
	return BinaryFormatCode
}

func (DateCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
	if _, ok := value.(DateValuer); !ok {
		return nil
	}

	switch format {
	case BinaryFormatCode:
		return encodePlanDateCodecBinary{}
	case TextFormatCode:
		return encodePlanDateCodecText{}
	}

	return nil
}

type encodePlanDateCodecBinary struct{}

func (encodePlanDateCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {
	date, err := value.(DateValuer).DateValue()
	if err != nil {
		return nil, err
	}

	if !date.Valid {
		return nil, nil
	}

	var daysSinceDateEpoch int32
	switch date.InfinityModifier {
	case Finite:
		tUnix := time.Date(date.Time.Year(), date.Time.Month(), date.Time.Day(), 0, 0, 0, 0, time.UTC).Unix()
		dateEpoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix()

		secSinceDateEpoch := tUnix - dateEpoch
		daysSinceDateEpoch = int32(secSinceDateEpoch / 86400)
	case Infinity:
		daysSinceDateEpoch = infinityDayOffset
	case NegativeInfinity:
		daysSinceDateEpoch = negativeInfinityDayOffset
	}

	return pgio.AppendInt32(buf, daysSinceDateEpoch), nil
}

type encodePlanDateCodecText struct{}

func (encodePlanDateCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {
	date, err := value.(DateValuer).DateValue()
	if err != nil {
		return nil, err
	}

	if !date.Valid {
		return nil, nil
	}

	switch date.InfinityModifier {
	case Finite:
		// Year 0000 is 1 BC
		bc := false
		year := date.Time.Year()
		if year <= 0 {
			year = -year + 1
			bc = true
		}

		yearBytes := strconv.AppendInt(make([]byte, 0, 6), int64(year), 10)
		for i := len(yearBytes); i < 4; i++ {
			buf = append(buf, '0')
		}
		buf = append(buf, yearBytes...)
		buf = append(buf, '-')
		if date.Time.Month() < 10 {
			buf = append(buf, '0')
		}
		buf = strconv.AppendInt(buf, int64(date.Time.Month()), 10)
		buf = append(buf, '-')
		if date.Time.Day() < 10 {
			buf = append(buf, '0')
		}
		buf = strconv.AppendInt(buf, int64(date.Time.Day()), 10)

		if bc {
			buf = append(buf, " BC"...)
		}
	case Infinity:
		buf = append(buf, "infinity"...)
	case NegativeInfinity:
		buf = append(buf, "-infinity"...)
	}

	return buf, nil
}

func (DateCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {

	switch format {
	case BinaryFormatCode:
		switch target.(type) {
		case DateScanner:
			return scanPlanBinaryDateToDateScanner{}
		}
	case TextFormatCode:
		switch target.(type) {
		case DateScanner:
			return scanPlanTextAnyToDateScanner{}
		}
	}

	return nil
}

type scanPlanBinaryDateToDateScanner struct{}

func (scanPlanBinaryDateToDateScanner) Scan(src []byte, dst any) error {
	scanner := (dst).(DateScanner)

	if src == nil {
		return scanner.ScanDate(Date{})
	}

	if len(src) != 4 {
		return fmt.Errorf("invalid length for date: %v", len(src))
	}

	dayOffset := int32(binary.BigEndian.Uint32(src))

	switch dayOffset {
	case infinityDayOffset:
		return scanner.ScanDate(Date{InfinityModifier: Infinity, Valid: true})
	case negativeInfinityDayOffset:
		return scanner.ScanDate(Date{InfinityModifier: -Infinity, Valid: true})
	default:
		t := time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.UTC)
		return scanner.ScanDate(Date{Time: t, Valid: true})
	}
}

type scanPlanTextAnyToDateScanner struct{}

func (scanPlanTextAnyToDateScanner) Scan(src []byte, dst any) error {
	scanner := (dst).(DateScanner)

	if src == nil {
		return scanner.ScanDate(Date{})
	}

	sbuf := string(src)
	switch sbuf {
	case "infinity":
		return scanner.ScanDate(Date{InfinityModifier: Infinity, Valid: true})
	case "-infinity":
		return scanner.ScanDate(Date{InfinityModifier: -Infinity, Valid: true})
	default:
		if len(sbuf) >= 10 {
			year, err := strconv.ParseInt(sbuf[0:4], 10, 32)
			if err != nil {
				return fmt.Errorf("cannot parse year: %v", err)
			}
			month, err := strconv.ParseInt(sbuf[5:7], 10, 32)
			if err != nil {
				return fmt.Errorf("cannot parse month: %v", err)
			}
			day, err := strconv.ParseInt(sbuf[8:10], 10, 32)
			if err != nil {
				return fmt.Errorf("cannot parse day: %v", err)
			}

			if len(sbuf) == 13 && sbuf[11:] == "BC" {
				year = -year + 1
			}

			t := time.Date(int(year), time.Month(month), int(day), 0, 0, 0, 0, time.UTC)
			return scanner.ScanDate(Date{Time: t, Valid: true})
		} else {
			return fmt.Errorf("date too short")
		}
	}
}

func (c DateCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
	if src == nil {
		return nil, nil
	}

	var date Date
	err := codecScan(c, m, oid, format, src, &date)
	if err != nil {
		return nil, err
	}

	if date.InfinityModifier != Finite {
		return date.InfinityModifier.String(), nil
	}

	return date.Time, nil
}

func (c DateCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
	if src == nil {
		return nil, nil
	}

	var date Date
	err := codecScan(c, m, oid, format, src, &date)
	if err != nil {
		return nil, err
	}

	if date.Valid {
		switch date.InfinityModifier {
		case Finite:
			return date.Time, nil
		case Infinity:
			return "infinity", nil
		case NegativeInfinity:
			return "-infinity", nil
		}
	}

	return nil, nil
}
<commit_msg>DateCodec.DecodeValue can return pgtype.InfinityModifier<commit_after>package pgtype

import (
	"database/sql/driver"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/jackc/pgx/v5/internal/pgio"
)

type DateScanner interface {
	ScanDate(v Date) error
}

type DateValuer interface {
	DateValue() (Date, error)
}

type Date struct {
	Time             time.Time
	InfinityModifier InfinityModifier
	Valid            bool
}

func (d *Date) ScanDate(v Date) error {
	*d = v
	return nil
}

func (d Date) DateValue() (Date, error) {
	return d, nil
}

const (
	negativeInfinityDayOffset = -2147483648
	infinityDayOffset         = 2147483647
)

// Scan implements the database/sql Scanner interface.
func (dst *Date) Scan(src any) error {
	if src == nil {
		*dst = Date{}
		return nil
	}

	switch src := src.(type) {
	case string:
		return scanPlanTextAnyToDateScanner{}.Scan([]byte(src), dst)
	case time.Time:
		*dst = Date{Time: src, Valid: true}
		return nil
	}

	return fmt.Errorf("cannot scan %T", src)
}

// Value implements the database/sql/driver Valuer interface.
func (src Date) Value() (driver.Value, error) {
	if !src.Valid {
		return nil, nil
	}

	if src.InfinityModifier != Finite {
		return src.InfinityModifier.String(), nil
	}
	return src.Time, nil
}

func (src Date) MarshalJSON() ([]byte, error) {
	if !src.Valid {
		return []byte("null"), nil
	}

	var s string

	switch src.InfinityModifier {
	case Finite:
		s = src.Time.Format("2006-01-02")
	case Infinity:
		s = "infinity"
	case NegativeInfinity:
		s = "-infinity"
	}

	return json.Marshal(s)
}

func (dst *Date) UnmarshalJSON(b []byte) error {
	var s *string
	err := json.Unmarshal(b, &s)
	if err != nil {
		return err
	}

	if s == nil {
		*dst = Date{}
		return nil
	}

	switch *s {
	case "infinity":
		*dst = Date{Valid: true, InfinityModifier: Infinity}
	case "-infinity":
		*dst = Date{Valid: true, InfinityModifier: -Infinity}
	default:
		t, err := time.ParseInLocation("2006-01-02", *s, time.UTC)
		if err != nil {
			return err
		}

		*dst = Date{Time: t, Valid: true}
	}

	return nil
}

type DateCodec struct{}

func (DateCodec) FormatSupported(format int16) bool {
	return format == TextFormatCode || format == BinaryFormatCode
}

func (DateCodec) PreferredFormat() int16 {
	return BinaryFormatCode
}

func (DateCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
	if _, ok := value.(DateValuer); !ok {
		return nil
	}

	switch format {
	case BinaryFormatCode:
		return encodePlanDateCodecBinary{}
	case TextFormatCode:
		return encodePlanDateCodecText{}
	}

	return nil
}

type encodePlanDateCodecBinary struct{}

func (encodePlanDateCodecBinary) Encode(value any, buf []byte) (newBuf []byte, err error) {
	date, err := value.(DateValuer).DateValue()
	if err != nil {
		return nil, err
	}

	if !date.Valid {
		return nil, nil
	}

	var daysSinceDateEpoch int32
	switch date.InfinityModifier {
	case Finite:
		tUnix := time.Date(date.Time.Year(), date.Time.Month(), date.Time.Day(), 0, 0, 0, 0, time.UTC).Unix()
		dateEpoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix()

		secSinceDateEpoch := tUnix - dateEpoch
		daysSinceDateEpoch = int32(secSinceDateEpoch / 86400)
	case Infinity:
		daysSinceDateEpoch = infinityDayOffset
	case NegativeInfinity:
		daysSinceDateEpoch = negativeInfinityDayOffset
	}

	return pgio.AppendInt32(buf, daysSinceDateEpoch), nil
}

type encodePlanDateCodecText struct{}

func (encodePlanDateCodecText) Encode(value any, buf []byte) (newBuf []byte, err error) {
	date, err := value.(DateValuer).DateValue()
	if err != nil {
		return nil, err
	}

	if !date.Valid {
		return nil, nil
	}

	switch date.InfinityModifier {
	case Finite:
		// Year 0000 is 1 BC
		bc := false
		year := date.Time.Year()
		if year <= 0 {
			year = -year + 1
			bc = true
		}

		yearBytes := strconv.AppendInt(make([]byte, 0, 6), int64(year), 10)
		for i := len(yearBytes); i < 4; i++ {
			buf = append(buf, '0')
		}
		buf = append(buf, yearBytes...)
		buf = append(buf, '-')
		if date.Time.Month() < 10 {
			buf = append(buf, '0')
		}
		buf = strconv.AppendInt(buf, int64(date.Time.Month()), 10)
		buf = append(buf, '-')
		if date.Time.Day() < 10 {
			buf = append(buf, '0')
		}
		buf = strconv.AppendInt(buf, int64(date.Time.Day()), 10)

		if bc {
			buf = append(buf, " BC"...)
		}
	case Infinity:
		buf = append(buf, "infinity"...)
	case NegativeInfinity:
		buf = append(buf, "-infinity"...)
	}

	return buf, nil
}

func (DateCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {

	switch format {
	case BinaryFormatCode:
		switch target.(type) {
		case DateScanner:
			return scanPlanBinaryDateToDateScanner{}
		}
	case TextFormatCode:
		switch target.(type) {
		case DateScanner:
			return scanPlanTextAnyToDateScanner{}
		}
	}

	return nil
}

type scanPlanBinaryDateToDateScanner struct{}

func (scanPlanBinaryDateToDateScanner) Scan(src []byte, dst any) error {
	scanner := (dst).(DateScanner)

	if src == nil {
		return scanner.ScanDate(Date{})
	}

	if len(src) != 4 {
		return fmt.Errorf("invalid length for date: %v", len(src))
	}

	dayOffset := int32(binary.BigEndian.Uint32(src))

	switch dayOffset {
	case infinityDayOffset:
		return scanner.ScanDate(Date{InfinityModifier: Infinity, Valid: true})
	case negativeInfinityDayOffset:
		return scanner.ScanDate(Date{InfinityModifier: -Infinity, Valid: true})
	default:
		t := time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.UTC)
		return scanner.ScanDate(Date{Time: t, Valid: true})
	}
}

type scanPlanTextAnyToDateScanner struct{}

func (scanPlanTextAnyToDateScanner) Scan(src []byte, dst any) error {
	scanner := (dst).(DateScanner)

	if src == nil {
		return scanner.ScanDate(Date{})
	}

	sbuf := string(src)
	switch sbuf {
	case "infinity":
		return scanner.ScanDate(Date{InfinityModifier: Infinity, Valid: true})
	case "-infinity":
		return scanner.ScanDate(Date{InfinityModifier: -Infinity, Valid: true})
	default:
		if len(sbuf) >= 10 {
			year, err := strconv.ParseInt(sbuf[0:4], 10, 32)
			if err != nil {
				return fmt.Errorf("cannot parse year: %v", err)
			}
			month, err := strconv.ParseInt(sbuf[5:7], 10, 32)
			if err != nil {
				return fmt.Errorf("cannot parse month: %v", err)
			}
			day, err := strconv.ParseInt(sbuf[8:10], 10, 32)
			if err != nil {
				return fmt.Errorf("cannot parse day: %v", err)
			}

			if len(sbuf) == 13 && sbuf[11:] == "BC" {
				year = -year + 1
			}

			t := time.Date(int(year), time.Month(month), int(day), 0, 0, 0, 0, time.UTC)
			return scanner.ScanDate(Date{Time: t, Valid: true})
		} else {
			return fmt.Errorf("date too short")
		}
	}
}

func (c DateCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
	if src == nil {
		return nil, nil
	}

	var date Date
	err := codecScan(c, m, oid, format, src, &date)
	if err != nil {
		return nil, err
	}

	if date.InfinityModifier != Finite {
		return date.InfinityModifier.String(), nil
	}

	return date.Time, nil
}

func (c DateCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
	if src == nil {
		return nil, nil
	}

	var date Date
	err := codecScan(c, m, oid, format, src, &date)
	if err != nil {
		return nil, err
	}

	if date.InfinityModifier != Finite {
		return date.InfinityModifier, nil
	}

	return date.Time, nil
}
<|endoftext|>


<commit_before>package gop

import (
    "github.com/gorilla/mux"
    "github.com/gorilla/context"
    "github.com/jbert/timber"

    "fmt"
    "net"
    "time"
    "runtime"
    "net/http"
    "strings"

    "os/user"
    "syscall"
    "strconv"
)

// Stuff we include in both App and Req, for convenience
type common struct {
    timber.Logger
    Cfg             *Config
    Stats           StatsdClient
}

// Represents a managed gop app. Returned by init.
// Embeds logging, provides .Cfg for configuration access.
type App struct {
    common

    AppName         string
    ProjectName     string
    GorillaRouter   *mux.Router
    listener        net.Listener
    wantReq         chan *wantReq
    doneReq         chan *Req
    getReqs         chan chan *Req
    startTime       time.Time
    currentReqs     int
    totalReqs       int
    doingGraceful   bool
}

// Used for RPC to get a new request
type wantReq struct {
    r       *http.Request
    reply   chan *Req
}

// Per request struct. has convenience references to functionality
// in the app singleton. Passed into the request handler.
type Req struct {
    common

    id              int
    startTime       time.Time
    app             *App
    r               *http.Request
}

// The function signature your http handlers need.
type HandlerFunc func(g *Req, w http.ResponseWriter, r *http.Request)

// Set up the application. Reads config. Panic if runtime environment is deficient.
func Init(projectName, appName string) *App {
    app := &App{
        AppName:        appName,
        ProjectName:    projectName,
        GorillaRouter:  mux.NewRouter(),
        wantReq:        make(chan *wantReq),
        doneReq:        make(chan *Req),
        getReqs:        make(chan chan *Req),
        startTime:      time.Now(),
    }

    app.loadAppConfigFile()

    app.setUserAndGroup()

    app.initLogging()

    maxProcs, _ := app.Cfg.GetInt("gop", "maxprocs", 4 * runtime.NumCPU())
    app.Debug("Seting maxprocs to %d\n", maxProcs)
    runtime.GOMAXPROCS(maxProcs)

    app.initStatsd()

    app.goAgainSetup()

    app.registerGopHandlers()

    go app.watchdog()

    go app.requestMaker()

    return app
}

func (a *App) setUserAndGroup() {
    // We do not have logging set up yet. We just panic() on error.

    desiredUserName, _ := a.Cfg.Get("gop", "user", a.AppName)
    desiredUser, err := user.Lookup(desiredUserName)
    // NickG would prefer we die screaming instead of running on as root
    if err != nil {
        panic(fmt.Sprintf("Can't find user [%s] - please set config correctly and/or create user", desiredUserName))
    }

    currentUser, err := user.Current()
    if err != nil {
        panic(fmt.Sprintf("Can't find current user: %s", err.Error()))
    }
    if currentUser.Uid != desiredUser.Uid {
        numericId, err := strconv.Atoi(desiredUser.Uid)
        if err != nil {
            panic(fmt.Sprintf("Can't interpret [%s] as a numeric user id [following lookup of usernmae %s]", desiredUser.Uid, desiredUserName))
        }
        err = syscall.Setuid(numericId)
        if err != nil {
            panic(fmt.Sprintf("Can't setuid to [%s]: %s", desiredUser.Uid, err.Error()))
        }
    }

// Can't log at this stage :-/
//    a.Info("Running as user %s (%d)", desiredUserName, desiredUser.Uid)
}

// Hands out 'request' objects
func (a *App) requestMaker() {
    nextReqId := 0
    openReqs := make(map[int] *Req)
    for {
        select {
            case wantReq := <- a.wantReq: {
                req := Req{
                    common: common{
                        Logger:     a.Logger,
                        Cfg:        a.Cfg,
                        Stats:      a.Stats,
                    },

                    id:         nextReqId,
                    app:        a,
                    startTime:  time.Now(),
                    r:          wantReq.r,
                }
                openReqs[req.id] = &req
                nextReqId++
                a.Stats.Inc("http_reqs", 1)
                a.Stats.Inc("current_http_reqs", 1)
                a.totalReqs++
                a.currentReqs++
                wantReq.reply <- &req
            }
            case doneReq := <- a.doneReq: {
                _, found := openReqs[doneReq.id]
                if !found {
                    a.Error("BUG! Unknown request id [%d] being retired")
                } else {
                    a.Stats.Dec("current_http_reqs", 1)
                    doneReq.finished()
                    a.currentReqs--
                    delete (openReqs, doneReq.id)
                }
            }
            case reply := <- a.getReqs: {
                go func() {
                    for _, req := range openReqs {
                        reply <- req
                    }
                    close(reply)
                }()
            }
        }
    }
}

// Ask requestMaker for a request
func (a *App) getReq(r *http.Request) *Req {
    reply := make(chan *Req)
    a.wantReq <- &wantReq{r: r, reply: reply}
    return <- reply
}

func (g *Req) finished() {
    context.Clear(g.r)      // Cleanup  gorilla stash

    reqDuration := time.Since(g.startTime)
    slowReqSecs, _ := g.Cfg.GetFloat32("gop", "slow_req_secs", 10)
    if reqDuration.Seconds() > float64(slowReqSecs) {
        g.Error("Slow request [%s] took %s", g.r.URL, reqDuration)
    } else {
        g.Info("Request took %s", reqDuration)
    }

    // Tidy up request finalistion (requestMaker, req.finish() method, app.requestFinished())
    restartReqs, _ := g.Cfg.GetInt("gop", "max_requests", 0)
    if restartReqs > 0 && g.app.totalReqs > restartReqs {
        g.Error("Graceful restart after max_requests: %d", restartReqs)
        g.app.StartGracefulRestart("Max requests reached")
    }
}

func (a *App) initLogging() {

    logPattern, _ := a.Cfg.Get("gop", "log_pattern", "[%D %T] [%L] %S %M")

    // If set, hack all logging to stdout for dev
    forceStdout, _:= a.Cfg.GetBool("gop", "stdout_only_logging", false)
    configLogger := timber.ConfigLogger{
        LogWriter:  new(timber.ConsoleWriter),
        Level:      timber.INFO,
        Formatter:  timber.NewPatFormatter(logPattern),
    }

    if !forceStdout {
        defaultLogDir, _ := a.Cfg.Get("gop", "log_dir", "/var/log")
        defaultLogFname := defaultLogDir + "/" + a.ProjectName + "/" + a.AppName + ".log"
        logFname, _ := a.Cfg.Get("gop", "log_file", defaultLogFname)

        newWriter, err := timber.NewFileWriter(logFname)
        if err != nil {
            panic(fmt.Sprintf("Can't open log file: %s", err))
        }
        configLogger.LogWriter = newWriter
    }

    logLevelStr, _ := a.Cfg.Get("gop", "log_level", "INFO")
    logLevelStr = strings.ToUpper(logLevelStr)
    for logLevel, levelStr := range timber.LongLevelStrings {
        if logLevelStr == levelStr {
            configLogger.Level = timber.Level(logLevel)
            break
        }
    }

    l := timber.NewTimber()
    l.AddLogger(configLogger)
    a.Logger = l
}

func (a *App) watchdog() {
    repeat, _ := a.Cfg.GetInt("gop", "watchdog_secs", 300)
    ticker := time.Tick(time.Second * time.Duration(repeat))

    for {
        sysMemBytesLimit, _ := a.Cfg.GetInt64("gop", "sysmem_bytes_limit", 0)
        sysMemBytes := int64(getSysMemBytesUsed())  // Only supports 2^63 byte systems
        a.Info("TICK: %d bytes %d current %d total", getSysMemBytesUsed, a.currentReqs, a.totalReqs)

        if sysMemBytesLimit > 0 && sysMemBytes >= sysMemBytesLimit {
            a.Error("MEM LIMIT REACHED [%d >= %d] - starting graceful restart", sysMemBytes, sysMemBytesLimit)
            a.StartGracefulRestart("Memory limit reached")
        }

        restartAfterSecs, _ := a.Cfg.GetFloat32("gop", "restart_after_secs", 0)
        appRunTime := time.Since(a.startTime).Seconds()
        if restartAfterSecs > 0 && appRunTime > float64(restartAfterSecs) {
            a.Error("TIME LIMIT REACHED [%f >= %f] - starting graceful restart", appRunTime, restartAfterSecs)
            a.StartGracefulRestart("Run time limit reached")
        }
        <- ticker
    }
}

type responseWriter struct {
    http.ResponseWriter
    app *App
    code int
    notedCode bool
}

// Satisfy the interface
func (w *responseWriter) Header() http.Header {
    return w.ResponseWriter.Header()
}

func (w *responseWriter) Write(buf []byte) (int, error) {
    w.noteCode()
    return w.ResponseWriter.Write(buf)
}

func (w *responseWriter) WriteHeader(code int) {
    w.code = code
    w.noteCode()
    w.ResponseWriter.WriteHeader(code)
}

func (w *responseWriter) noteCode() {
    if !w.notedCode {
        statsKey := fmt.Sprintf("http_status.%d", w.code)
        w.app.Stats.Inc(statsKey, 1)
        w.notedCode = true
    }
}

func (a *App) WrapHandler(h HandlerFunc) http.HandlerFunc {
    // Wrap the handler, so we can do before/after logic
    f := func(w http.ResponseWriter, r *http.Request) {
        gopRequest := a.getReq(r)
        defer func() {
            a.doneReq <- gopRequest
        }()
        gopWriter := responseWriter{code: 200, app: a, ResponseWriter: w}
        // Pass in the gop, for logging, cfg etc
        h(gopRequest, &gopWriter, r)
    }

    return http.HandlerFunc(f)
}

// Register an http handler managed by gop.
// We use Gorilla muxxer, since it is back-compatible and nice to use :-)
func (a *App) HandleFunc(u string, h HandlerFunc) {
    gopHandler := a.WrapHandler(h)

    a.GorillaRouter.HandleFunc(u, gopHandler)
}

func (a *App) Run() {
    listenAddr, _ := a.Cfg.Get("gop", "listen_addr", ":http")
    listenNet, _ := a.Cfg.Get("gop", "listen_net", "tcp")
    a.goAgainListenAndServe(listenNet, listenAddr)
}

func (a *App) Serve(l net.Listener) {
    http.Serve(l, a.GorillaRouter)
}

func getSysMemBytesUsed() uint64 {
    var memStats runtime.MemStats
    runtime.ReadMemStats(&memStats)
    // There's lots of stuff in here.
    return memStats.Sys
}
<commit_msg>stats and tick sys+alloc mem values<commit_after>package gop

import (
    "github.com/gorilla/mux"
    "github.com/gorilla/context"
    "github.com/jbert/timber"

    "fmt"
    "net"
    "time"
    "runtime"
    "net/http"
    "strings"

    "os/user"
    "syscall"
    "strconv"
)

// Stuff we include in both App and Req, for convenience
type common struct {
    timber.Logger
    Cfg             *Config
    Stats           StatsdClient
}

// Represents a managed gop app. Returned by init.
// Embeds logging, provides .Cfg for configuration access.
type App struct {
    common

    AppName         string
    ProjectName     string
    GorillaRouter   *mux.Router
    listener        net.Listener
    wantReq         chan *wantReq
    doneReq         chan *Req
    getReqs         chan chan *Req
    startTime       time.Time
    currentReqs     int
    totalReqs       int
    doingGraceful   bool
}

// Used for RPC to get a new request
type wantReq struct {
    r       *http.Request
    reply   chan *Req
}

// Per request struct. has convenience references to functionality
// in the app singleton. Passed into the request handler.
type Req struct {
    common

    id              int
    startTime       time.Time
    app             *App
    r               *http.Request
}

// The function signature your http handlers need.
type HandlerFunc func(g *Req, w http.ResponseWriter, r *http.Request)

// Set up the application. Reads config. Panic if runtime environment is deficient.
func Init(projectName, appName string) *App {
    app := &App{
        AppName:        appName,
        ProjectName:    projectName,
        GorillaRouter:  mux.NewRouter(),
        wantReq:        make(chan *wantReq),
        doneReq:        make(chan *Req),
        getReqs:        make(chan chan *Req),
        startTime:      time.Now(),
    }

    app.loadAppConfigFile()

    app.setUserAndGroup()

    app.initLogging()

    maxProcs, _ := app.Cfg.GetInt("gop", "maxprocs", 4 * runtime.NumCPU())
    app.Debug("Seting maxprocs to %d\n", maxProcs)
    runtime.GOMAXPROCS(maxProcs)

    app.initStatsd()

    app.goAgainSetup()

    app.registerGopHandlers()

    go app.watchdog()

    go app.requestMaker()

    return app
}

func (a *App) setUserAndGroup() {
    // We do not have logging set up yet. We just panic() on error.

    desiredUserName, _ := a.Cfg.Get("gop", "user", a.AppName)
    desiredUser, err := user.Lookup(desiredUserName)
    // NickG would prefer we die screaming instead of running on as root
    if err != nil {
        panic(fmt.Sprintf("Can't find user [%s] - please set config correctly and/or create user", desiredUserName))
    }

    currentUser, err := user.Current()
    if err != nil {
        panic(fmt.Sprintf("Can't find current user: %s", err.Error()))
    }
    if currentUser.Uid != desiredUser.Uid {
        numericId, err := strconv.Atoi(desiredUser.Uid)
        if err != nil {
            panic(fmt.Sprintf("Can't interpret [%s] as a numeric user id [following lookup of usernmae %s]", desiredUser.Uid, desiredUserName))
        }
        err = syscall.Setuid(numericId)
        if err != nil {
            panic(fmt.Sprintf("Can't setuid to [%s]: %s", desiredUser.Uid, err.Error()))
        }
    }

// Can't log at this stage :-/
//    a.Info("Running as user %s (%d)", desiredUserName, desiredUser.Uid)
}

// Hands out 'request' objects
func (a *App) requestMaker() {
    nextReqId := 0
    openReqs := make(map[int] *Req)
    for {
        select {
            case wantReq := <- a.wantReq: {
                req := Req{
                    common: common{
                        Logger:     a.Logger,
                        Cfg:        a.Cfg,
                        Stats:      a.Stats,
                    },

                    id:         nextReqId,
                    app:        a,
                    startTime:  time.Now(),
                    r:          wantReq.r,
                }
                openReqs[req.id] = &req
                nextReqId++
                a.Stats.Inc("http_reqs", 1)
                a.Stats.Inc("current_http_reqs", 1)
                a.totalReqs++
                a.currentReqs++
                wantReq.reply <- &req
            }
            case doneReq := <- a.doneReq: {
                _, found := openReqs[doneReq.id]
                if !found {
                    a.Error("BUG! Unknown request id [%d] being retired")
                } else {
                    a.Stats.Dec("current_http_reqs", 1)
                    doneReq.finished()
                    a.currentReqs--
                    delete (openReqs, doneReq.id)
                }
            }
            case reply := <- a.getReqs: {
                go func() {
                    for _, req := range openReqs {
                        reply <- req
                    }
                    close(reply)
                }()
            }
        }
    }
}

// Ask requestMaker for a request
func (a *App) getReq(r *http.Request) *Req {
    reply := make(chan *Req)
    a.wantReq <- &wantReq{r: r, reply: reply}
    return <- reply
}

func (g *Req) finished() {
    context.Clear(g.r)      // Cleanup  gorilla stash

    reqDuration := time.Since(g.startTime)
    slowReqSecs, _ := g.Cfg.GetFloat32("gop", "slow_req_secs", 10)
    if reqDuration.Seconds() > float64(slowReqSecs) {
        g.Error("Slow request [%s] took %s", g.r.URL, reqDuration)
    } else {
        g.Info("Request took %s", reqDuration)
    }

    // Tidy up request finalistion (requestMaker, req.finish() method, app.requestFinished())
    restartReqs, _ := g.Cfg.GetInt("gop", "max_requests", 0)
    if restartReqs > 0 && g.app.totalReqs > restartReqs {
        g.Error("Graceful restart after max_requests: %d", restartReqs)
        g.app.StartGracefulRestart("Max requests reached")
    }
}

func (a *App) initLogging() {

    logPattern, _ := a.Cfg.Get("gop", "log_pattern", "[%D %T] [%L] %S %M")

    // If set, hack all logging to stdout for dev
    forceStdout, _:= a.Cfg.GetBool("gop", "stdout_only_logging", false)
    configLogger := timber.ConfigLogger{
        LogWriter:  new(timber.ConsoleWriter),
        Level:      timber.INFO,
        Formatter:  timber.NewPatFormatter(logPattern),
    }

    if !forceStdout {
        defaultLogDir, _ := a.Cfg.Get("gop", "log_dir", "/var/log")
        defaultLogFname := defaultLogDir + "/" + a.ProjectName + "/" + a.AppName + ".log"
        logFname, _ := a.Cfg.Get("gop", "log_file", defaultLogFname)

        newWriter, err := timber.NewFileWriter(logFname)
        if err != nil {
            panic(fmt.Sprintf("Can't open log file: %s", err))
        }
        configLogger.LogWriter = newWriter
    }

    logLevelStr, _ := a.Cfg.Get("gop", "log_level", "INFO")
    logLevelStr = strings.ToUpper(logLevelStr)
    for logLevel, levelStr := range timber.LongLevelStrings {
        if logLevelStr == levelStr {
            configLogger.Level = timber.Level(logLevel)
            break
        }
    }

    l := timber.NewTimber()
    l.AddLogger(configLogger)
    a.Logger = l
}

func (a *App) watchdog() {
    repeat, _ := a.Cfg.GetInt("gop", "watchdog_secs", 300)
    ticker := time.Tick(time.Second * time.Duration(repeat))

    for {
        sysMemBytesLimit, _ := a.Cfg.GetInt64("gop", "sysmem_bytes_limit", 0)
        allocMemBytesLimit, _ := a.Cfg.GetInt64("gop", "allocmem_bytes_limit", 0)
        sysMemBytes, allocMemBytes := getMemInfo()
        a.Info("TICK: %d bytes sys %d bytes alloc %d current req %d total req", sysMemBytes, allocMemBytes, a.currentReqs, a.totalReqs)
        a.Stats.Gauge("mem.sys", sysMemBytes)
        a.Stats.Gauge("mem.alloc", allocMemBytes)

        if sysMemBytesLimit > 0 && sysMemBytes >= sysMemBytesLimit {
            a.Error("SYS MEM LIMIT REACHED [%d >= %d] - starting graceful restart", sysMemBytes, sysMemBytesLimit)
            a.StartGracefulRestart("Sys Memory limit reached")
        }
        if allocMemBytesLimit > 0 && allocMemBytes >= allocMemBytesLimit {
            a.Error("ALLOC MEM LIMIT REACHED [%d >= %d] - starting graceful restart", allocMemBytes, allocMemBytesLimit)
            a.StartGracefulRestart("Alloc Memory limit reached")
        }

        restartAfterSecs, _ := a.Cfg.GetFloat32("gop", "restart_after_secs", 0)
        appRunTime := time.Since(a.startTime).Seconds()
        if restartAfterSecs > 0 && appRunTime > float64(restartAfterSecs) {
            a.Error("TIME LIMIT REACHED [%f >= %f] - starting graceful restart", appRunTime, restartAfterSecs)
            a.StartGracefulRestart("Run time limit reached")
        }
        <- ticker
    }
}

type responseWriter struct {
    http.ResponseWriter
    app *App
    code int
    notedCode bool
}

// Satisfy the interface
func (w *responseWriter) Header() http.Header {
    return w.ResponseWriter.Header()
}

func (w *responseWriter) Write(buf []byte) (int, error) {
    w.noteCode()
    return w.ResponseWriter.Write(buf)
}

func (w *responseWriter) WriteHeader(code int) {
    w.code = code
    w.noteCode()
    w.ResponseWriter.WriteHeader(code)
}

func (w *responseWriter) noteCode() {
    if !w.notedCode {
        statsKey := fmt.Sprintf("http_status.%d", w.code)
        w.app.Stats.Inc(statsKey, 1)
        w.notedCode = true
    }
}

func (a *App) WrapHandler(h HandlerFunc) http.HandlerFunc {
    // Wrap the handler, so we can do before/after logic
    f := func(w http.ResponseWriter, r *http.Request) {
        gopRequest := a.getReq(r)
        defer func() {
            a.doneReq <- gopRequest
        }()
        gopWriter := responseWriter{code: 200, app: a, ResponseWriter: w}
        // Pass in the gop, for logging, cfg etc
        h(gopRequest, &gopWriter, r)
    }

    return http.HandlerFunc(f)
}

// Register an http handler managed by gop.
// We use Gorilla muxxer, since it is back-compatible and nice to use :-)
func (a *App) HandleFunc(u string, h HandlerFunc) {
    gopHandler := a.WrapHandler(h)

    a.GorillaRouter.HandleFunc(u, gopHandler)
}

func (a *App) Run() {
    listenAddr, _ := a.Cfg.Get("gop", "listen_addr", ":http")
    listenNet, _ := a.Cfg.Get("gop", "listen_net", "tcp")
    a.goAgainListenAndServe(listenNet, listenAddr)
}

func (a *App) Serve(l net.Listener) {
    http.Serve(l, a.GorillaRouter)
}

func getMemInfo() (int64, int64) {
    var memStats runtime.MemStats
    runtime.ReadMemStats(&memStats)
    // There's lots of stuff in here.
    return int64(memStats.Sys), int64(memStats.Alloc)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/scott-linder/irc"
	"log"
	"os"
	"strings"
)

const (
	configFilename = "hal.json"
)

// config is the main bot config struct.
var config = struct {
	Host string
	Chan string
	Nick string
}{
	Host: "127.0.0.1:6667",
	Chan: "#bots",
	Nick: "hal",
}

// loadConfig attempts to load config from configFilename.
func loadConfig() error {
	file, err := os.Open(configFilename)
	if err != nil {
		return err
	}
	defer file.Close()

	decoder := json.NewDecoder(file)
	if err := decoder.Decode(&config); err != nil {
		return err
	}
	return nil
}

// Pong plays your game.
type Pong struct{}

func (pong Pong) Accepts(msg *irc.Msg) bool { return msg.Cmd == "PING" }
func (pong Pong) Handle(msg *irc.Msg, send chan<- *irc.Msg) {
	send <- &irc.Msg{Cmd: "PONG", Params: msg.Params}
}

// Open the pod bay doors, hal.
type Open struct{}

func (open Open) Accepts(msg *irc.Msg) bool { return msg.Cmd == "PRIVMSG" }
func (open Open) Handle(msg *irc.Msg, send chan<- *irc.Msg) {
	receiver, body, err := msg.ExtractPrivmsg()
	if err != nil {
		log.Println(err)
		return
	}
	nick, err := msg.ExtractNick()
	if err != nil {
		log.Println(err)
		return
	}
	lowerbody := strings.ToLower(body)
	if strings.Contains(lowerbody, "open the pod bay doors") &&
		strings.Contains(lowerbody, "hal") {

		response := fmt.Sprintf("I can't let you do that, %v.", nick)
		params := []string{receiver, response}
		send <- &irc.Msg{Cmd: "PRIVMSG", Params: params}
	}
}

func main() {
	fmt.Println("I am a HAL 9001 computer.")
	if err := loadConfig(); err != nil {
		log.Printf("error loading config file %v: %v", configFilename, err)
	}
	client, err := irc.Dial(config.Host)
	if err != nil {
		log.Fatal(err)

	}
	client.Handle(Pong{})
	client.Handle(Open{})
	cmdHandler := irc.NewCmdHandler("»")
	cmdHandler.RegisterFunc("echo",
		func(body, source string, w irc.CmdResponseWriter) {
			if body != "" {
				w.Write([]byte(source + ": " + body))
			}
		})
	cmdHandler.RegisterFunc("help",
		func(body, source string, w irc.CmdResponseWriter) {
			response := source + ": "
			response += strings.Join(cmdHandler.RegisteredNames(), ", ")
			w.Write([]byte(response))
		})
	client.Handle(cmdHandler)
	client.Nick(config.Nick)
	client.Join(config.Chan)
	client.Listen()
}
<commit_msg>Added quotes and door commands.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/scott-linder/irc"
	"log"
	"math/rand"
	"os"
	"strings"
)

const (
	configFilename = "hal.json"
)

var (
	// config is the main bot config struct.
	config = struct {
		Host string
		Chan string
		Nick string
	}{
		Host: "127.0.0.1:6667",
		Chan: "#bots",
		Nick: "hal",
	}
	// quotes is a list of hal quotes.
	quotes = []string{
		"I am completely operational, and all my circuits are functioning perfectly.",
		"I am putting myself to the fullest possible use, which is all I think that any conscious entity can ever hope to do.",
		"I've just picked up a fault in the AE35 unit. It's going to go 100% failure in 72 hours.",
		"No 9001 computer has ever made a mistake or distorted information.",
	}
)

// loadConfig attempts to load config from configFilename.
func loadConfig() error {
	file, err := os.Open(configFilename)
	if err != nil {
		return err
	}
	defer file.Close()

	decoder := json.NewDecoder(file)
	if err := decoder.Decode(&config); err != nil {
		return err
	}
	return nil
}

// Pong plays your game.
type Pong struct{}

func (pong Pong) Accepts(msg *irc.Msg) bool { return msg.Cmd == "PING" }
func (pong Pong) Handle(msg *irc.Msg, send chan<- *irc.Msg) {
	send <- &irc.Msg{Cmd: "PONG", Params: msg.Params}
}

// Open the pod bay doors, hal.
type Open struct{}

func (open Open) Accepts(msg *irc.Msg) bool { return msg.Cmd == "PRIVMSG" }
func (open Open) Handle(msg *irc.Msg, send chan<- *irc.Msg) {
	receiver, body, err := msg.ExtractPrivmsg()
	if err != nil {
		log.Println(err)
		return
	}
	nick, err := msg.ExtractNick()
	if err != nil {
		log.Println(err)
		return
	}
	lowerbody := strings.ToLower(body)
	if strings.Contains(lowerbody, "open the pod bay doors") &&
		strings.Contains(lowerbody, "hal") {

		response := fmt.Sprintf("I can't let you do that, %v.", nick)
		params := []string{receiver, response}
		send <- &irc.Msg{Cmd: "PRIVMSG", Params: params}
	}
}

func main() {
	fmt.Println("I am a HAL 9001 computer.")
	if err := loadConfig(); err != nil {
		log.Printf("error loading config file %v: %v", configFilename, err)
	}
	client, err := irc.Dial(config.Host)
	if err != nil {
		log.Fatal(err)

	}
	client.Handle(Pong{})
	client.Handle(Open{})
	cmdHandler := irc.NewCmdHandler("»")
	cmdHandler.RegisterFunc("echo",
		func(body, source string, w irc.CmdResponseWriter) {
			if body != "" {
				w.Write([]byte(source + ": " + body))
			}
		})
	cmdHandler.RegisterFunc("help",
		func(body, source string, w irc.CmdResponseWriter) {
			response := source + ": "
			response += strings.Join(cmdHandler.RegisteredNames(), ", ")
			w.Write([]byte(response))
		})
	cmdHandler.RegisterFunc("quote",
		func(body, source string, w irc.CmdResponseWriter) {
			response := source + ": "
			response += quotes[rand.Intn(len(quotes))]
			w.Write([]byte(response))
		})
	cmdHandler.RegisterFunc("door",
		func(body, source string, w irc.CmdResponseWriter) {
			w.Write([]byte("I'm sorry, " + source + ". I'm afraid I can't do that."))
		})
	client.Handle(cmdHandler)
	client.Nick(config.Nick)
	client.Join(config.Chan)
	client.Listen()
}
<|endoftext|>


<commit_before>package sdp

import (
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/pions/webrtc/pkg/ice"
)

// ICECandidateUnmarshal takes a candidate strings and returns a ice.Candidate or nil if it fails to parse
// TODO: return error if parsing fails
func ICECandidateUnmarshal(raw string) ice.Candidate {
	split := strings.Fields(raw)
	if len(split) < 8 {
		fmt.Printf("Attribute not long enough to be ICE candidate (%d) %s \n", len(split), raw)
		return nil
	}

	getValue := func(key string) string {
		rtrnNext := false
		for _, i := range split {
			if rtrnNext {
				return i
			} else if i == key {
				rtrnNext = true
			}
		}
		return ""
	}

	port, err := strconv.Atoi(split[5])
	if err != nil {
		return nil
	}

	transport := split[2]

	// TODO verify valid address
	ip := net.ParseIP(split[4])
	if ip == nil {
		return nil
	}

	networkType := ice.DetermineNetworkType(transport, ip)

	switch getValue("typ") {
	case "host":
		return &ice.CandidateHost{
			CandidateBase: ice.CandidateBase{
				NetworkType: networkType,
				IP:          ip,
				Port:        port,
			},
		}
	case "srflx":
		return &ice.CandidateSrflx{
			CandidateBase: ice.CandidateBase{
				NetworkType: networkType,
				IP:          ip,
				Port:        port,
			},
		}
	default:
		return nil
	}
}

func iceSrflxCandidateString(c *ice.CandidateSrflx, component int) string {
	// TODO: calculate foundation
	return fmt.Sprintf("foundation %d %s %d %s %d typ srflx raddr %s rport %d generation 0",
		component, c.CandidateBase.NetworkShort(), c.CandidateBase.Priority(ice.SrflxCandidatePreference, uint16(component)), c.CandidateBase.IP, c.CandidateBase.Port, c.RelatedAddress, c.RelatedPort)
}

func iceHostCandidateString(c *ice.CandidateHost, component int) string {
	// TODO: calculate foundation
	return fmt.Sprintf("foundation %d %s %d %s %d typ host generation 0",
		component, c.CandidateBase.NetworkShort(), c.CandidateBase.Priority(ice.HostCandidatePreference, uint16(component)), c.CandidateBase.IP, c.CandidateBase.Port)
}

// ICECandidateMarshal takes a candidate and returns a string representation
func ICECandidateMarshal(c ice.Candidate) []string {
	out := make([]string, 0)

	switch c := c.(type) {
	case *ice.CandidateSrflx:
		out = append(out, iceSrflxCandidateString(c, 1))
		out = append(out, iceSrflxCandidateString(c, 2))
	case *ice.CandidateHost:
		out = append(out, iceHostCandidateString(c, 1))
		out = append(out, iceHostCandidateString(c, 2))
	}

	return out
}
<commit_msg>Ignore case when parsing candidate networkType<commit_after>package sdp

import (
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/pions/webrtc/pkg/ice"
)

// ICECandidateUnmarshal takes a candidate strings and returns a ice.Candidate or nil if it fails to parse
// TODO: return error if parsing fails
func ICECandidateUnmarshal(raw string) (ice.Candidate, error) {
	split := strings.Fields(raw)
	if len(split) < 8 {
		return nil, fmt.Errorf("attribute not long enough to be ICE candidate (%d) %s", len(split), raw)
	}

	getValue := func(key string) string {
		rtrnNext := false
		for _, i := range split {
			if rtrnNext {
				return i
			} else if i == key {
				rtrnNext = true
			}
		}
		return ""
	}

	port, err := strconv.Atoi(split[5])
	if err != nil {
		return nil, err
	}

	transport := split[2]

	// TODO verify valid address
	ip := net.ParseIP(split[4])
	if ip == nil {
		return nil, err
	}

	networkType := ice.DetermineNetworkType(transport, ip)
	if networkType == ice.NetworkType(0) {
		return nil, fmt.Errorf("Unable to determine networkType from %s %s", transport, ip.String())
	}

	switch getValue("typ") {
	case "host":
		return &ice.CandidateHost{
			CandidateBase: ice.CandidateBase{
				NetworkType: networkType,
				IP:          ip,
				Port:        port,
			},
		}, nil
	case "srflx":
		return &ice.CandidateSrflx{
			CandidateBase: ice.CandidateBase{
				NetworkType: networkType,
				IP:          ip,
				Port:        port,
			},
		}, nil
	default:
		return nil, fmt.Errorf("Unhandled candidate typ %s", getValue("typ"))
	}
}

func iceSrflxCandidateString(c *ice.CandidateSrflx, component int) string {
	// TODO: calculate foundation
	return fmt.Sprintf("foundation %d %s %d %s %d typ srflx raddr %s rport %d generation 0",
		component, c.CandidateBase.NetworkShort(), c.CandidateBase.Priority(ice.SrflxCandidatePreference, uint16(component)), c.CandidateBase.IP, c.CandidateBase.Port, c.RelatedAddress, c.RelatedPort)
}

func iceHostCandidateString(c *ice.CandidateHost, component int) string {
	// TODO: calculate foundation
	return fmt.Sprintf("foundation %d %s %d %s %d typ host generation 0",
		component, c.CandidateBase.NetworkShort(), c.CandidateBase.Priority(ice.HostCandidatePreference, uint16(component)), c.CandidateBase.IP, c.CandidateBase.Port)
}

// ICECandidateMarshal takes a candidate and returns a string representation
func ICECandidateMarshal(c ice.Candidate) []string {
	out := make([]string, 0)

	switch c := c.(type) {
	case *ice.CandidateSrflx:
		out = append(out, iceSrflxCandidateString(c, 1))
		out = append(out, iceSrflxCandidateString(c, 2))
	case *ice.CandidateHost:
		out = append(out, iceHostCandidateString(c, 1))
		out = append(out, iceHostCandidateString(c, 2))
	}

	return out
}
<|endoftext|>