<commit_before>// Package gen provides functions for generating go source code
//
// The gen package provides wrapper functions around the go/ast and
// go/token packages to reduce boilerplate.
package gen

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"reflect"
	"strconv"
	"strings"
	"text/template"

	"golang.org/x/tools/imports"
)

// TypeDecl generates a type declaration with the given name.
func TypeDecl(name *ast.Ident, typ ast.Expr) *ast.GenDecl {
	return &ast.GenDecl{
		Tok: token.TYPE,
		Specs: []ast.Spec{
			&ast.TypeSpec{
				Name: name,
				Type: typ,
			},
		},
	}
}

// Sanitize modifies any names that are reserved in
// Go, so that they may be used as identifiers without
// causing a syntax error.
func Sanitize(name string) string {
	switch name {
	case "break", "default", "func", "interface", "select",
		"case", "defer", "go", "map", "struct",
		"chan", "else", "goto", "package", "switch",
		"const", "fallthrough", "if", "range", "type",
		"continue", "for", "import", "return", "var":
		return name + "_"
	}
	return name
}

// ToString converts the expression to a Go source string.
func ToString(expr ast.Expr) (string, error) {
	var buf bytes.Buffer
	err := format.Node(&buf, token.NewFileSet(), expr)
	return buf.String(), err
}

// Struct creates a struct{} expression. The arguments are a series
// of name/type/tag tuples. Name must be of type *ast.Ident, type
// must be of type ast.Expr, and tag must be of type *ast.BasicLit,
// The number of arguments must be a multiple of 3, or a run-time
// panic will occur.
func Struct(args ...ast.Expr) *ast.StructType {
	fields := new(ast.FieldList)
	if len(args)%3 != 0 {
		panic("Number of args to FieldList must be a multiple of 3, got " + strconv.Itoa(len(args)))
	}
	for i := 0; i < len(args); i += 3 {
		var field ast.Field
		name, typ, tag := args[i], args[i+1], args[i+2]
		if name != nil {
			field.Names = []*ast.Ident{name.(*ast.Ident)}
		}
		if typ != nil {
			field.Type = typ
		}
		if tag != nil {
			field.Tag = tag.(*ast.BasicLit)
		}
		fields.List = append(fields.List, &field)
	}
	return &ast.StructType{Fields: fields}
}

// FieldList generates a field list from strings in the form "[name]
// expr".
func FieldList(fields ...string) (*ast.FieldList, error) {
	result := &ast.FieldList{List: []*ast.Field{}}
	for _, s := range fields {
		parts := strings.SplitN(s, " ", 2)
		if len(parts) == 0 {
			return nil, fmt.Errorf("empty field list item %q", s)
		}
		var names []*ast.Ident
		typeExpr, err := parser.ParseExpr(parts[len(parts)-1])
		if err != nil {
			return nil, fmt.Errorf("could not parse type in %q: %v", s, err)
		}
		if len(parts) > 1 {
			names = []*ast.Ident{ast.NewIdent(parts[0])}
		}
		result.List = append(result.List, &ast.Field{
			Names: names,
			Type:  typeExpr,
		})
	}
	return result, nil
}

// String generates a literal string. If the string contains a double
// quote, backticks are used for quoting instead.
func String(s string) *ast.BasicLit {
	if strings.Contains(s, "\"") && !strings.Contains(s, "`") {
		return &ast.BasicLit{Kind: token.STRING, Value: "`" + s + "`"}
	}
	return &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(s)}
}

// Public turns a string into a public (uppercase)
// identifier.
func Public(name string) *ast.Ident {
	return ast.NewIdent(strings.Title(name))
}

func constDecl(kind token.Token, args ...string) *ast.GenDecl {
	decl := ast.GenDecl{Tok: token.CONST}

	if len(args)%3 != 0 {
		panic("Number of values passed to ConstString must be a multiple of 3")
	}
	for i := 0; i < len(args); i += 3 {
		name, typ, val := args[i], args[i+1], args[i+2]
		lit := &ast.BasicLit{Kind: kind}
		if kind == token.STRING {
			lit.Value = strconv.Quote(val)
		} else {
			lit.Value = val
		}
		a := &ast.ValueSpec{
			Names:  []*ast.Ident{ast.NewIdent(name)},
			Values: []ast.Expr{lit},
		}
		if typ != "" {
			a.Type = ast.NewIdent(typ)
		}
		decl.Specs = append(decl.Specs, a)
	}

	if len(decl.Specs) > 1 {
		decl.Lparen = 1
	}

	return &decl
}

// SimpleType creates an identifier suitable
// for use as a type expression.
func SimpleType(name string) ast.Expr {
	return ast.NewIdent(name)
}

// ConstInt creates a series of numeric const declarations from
// the name/value pairs in args.
func ConstInt(args ...string) *ast.GenDecl {
	return constDecl(token.INT, args...)
}

// ConstString creates a series of string const declarations from
// the name/value pairs in args.
func ConstString(args ...string) *ast.GenDecl {
	return constDecl(token.STRING, args...)
}

// ConstFloat creates a series of floating-point const
// declarations from the name/value pairs in args.
func ConstFloat(args ...string) *ast.GenDecl {
	return constDecl(token.FLOAT, args...)
}

// ConstChar creates a series of character const
// declarations from the name/value pairs in args.
func ConstChar(args ...string) *ast.GenDecl {
	return constDecl(token.CHAR, args...)
}

// ConstImaginary creates a series of imaginary const
// declarations from the name/value pairs in args.
func ConstImaginary(args ...string) *ast.GenDecl {
	return constDecl(token.IMAG, args...)
}

// PackageDoc inserts package-level comments into a file,
// preceding the "package" statement.
func PackageDoc(file *ast.File, comments ...string) *ast.File {
	if len(comments) == 0 {
		return file
	}
	file.Doc = CommentGroup(comments...)
	return file
}

// CommentGroup creates a comment group from strings.
func CommentGroup(comments ...string) *ast.CommentGroup {
	var group ast.CommentGroup
	for _, v := range comments {
		line := bufio.NewScanner(strings.NewReader(v))
		for line.Scan() {
			group.List = append(group.List, &ast.Comment{
				Text: "// " + strings.TrimSpace(line.Text()),
			})
		}
	}
	return &group
}

type Function struct {
	name, receiver, godoc string
	args, returns         []string
	err                   error
	body                  string
}

// Name returns the name of the function.
func (fn *Function) Name() string {
	return fn.name
}

func Func(name string) *Function {
	return &Function{name: name}
}

// Decl generates Go source for a Func.  an error is returned if the
// body, or parameters cannot be parsed.
func (fn *Function) Decl() (*ast.FuncDecl, error) {
	var err error
	var comments *ast.CommentGroup

	if fn.err != nil {
		return nil, fn.err
	}
	if fn.name == "" {
		return nil, errors.New("function name unset")
	}
	if len(fn.body) == 0 {
		return nil, fmt.Errorf("function body for %s unset")
	}

	if fn.godoc != "" {
		comments = &ast.CommentGroup{List: []*ast.Comment{}}
		for _, line := range strings.Split(fn.godoc, "\n") {
			comments.List = append(comments.List, &ast.Comment{
				Text: "// " + line + "\n",
			})
		}
	}
	fl := func(args ...string) (list *ast.FieldList) {
		if len(args) == 0 || len(args[0]) == 0 || err != nil {
			return nil
		}
		list, err = FieldList(args...)
		return list
	}
	args := fl(fn.args...)
	returns := fl(fn.returns...)
	receiver := fl(fn.receiver)
	if err != nil {
		return nil, err
	}
	body, err := parseBlock(fn.body)
	if err != nil {
		return nil, fmt.Errorf("could not parse function body of %s: %v in\n%s", fn.name, err, fn.body)
	}
	return &ast.FuncDecl{
		Doc:  comments,
		Recv: receiver,
		Name: ast.NewIdent(fn.name),
		Type: &ast.FuncType{
			Params:  args,
			Results: returns,
		},
		Body: body,
	}, nil
}

// Body sets the body of a function. The body should not include
// enclosing braces.
func (fn *Function) Body(format string, v ...interface{}) *Function {
	fn.body = fmt.Sprintf(format, v...)
	return fn
}

// BodyTmpl allows use of the text/template package to construct
// the body of a function.
func (fn *Function) BodyTmpl(tmpl string, dot interface{}) *Function {
	var buf bytes.Buffer
	t, err := template.New(fn.Name()).Funcs(template.FuncMap{
		"title":    strings.Title,
		"split":    strings.Split,
		"join":     strings.Join,
		"sanitize": Sanitize,
	}).Parse(tmpl)
	if err != nil {
		fn.err = err
	} else if err := t.Execute(&buf, dot); err != nil {
		fn.err = err
	} else {
		fn.body = buf.String()
	}
	return fn
}

// Returns sets the return values of a function. Each return
// value should be a string matching the Go syntax for a
// single return value.
func (fn *Function) Returns(values ...string) *Function {
	fn.returns = values
	return fn
}

// Comments sets the Godoc comments for the function.
func (fn *Function) Comment(s string) *Function {
	fn.godoc = s
	return fn
}

// Args sets the arguments that a function takes.
func (fn *Function) Args(args ...string) *Function {
	fn.args = args
	return fn
}

// Receiver turns the function into a method operating on
// the specified type.
func (fn *Function) Receiver(receiver string) *Function {
	fn.receiver = receiver
	return fn
}

// Declarations parses a list of Go source code blocks and converts
// them into *ast.Decl values. If a parsing error occurs, it is returned
// immediately and no further parsing takes place.
func Declarations(blocks ...string) ([]ast.Decl, error) {
	var buf bytes.Buffer
	decls := make([]ast.Decl, 0, len(blocks))
	for _, block := range blocks {
		fmt.Fprintf(&buf, "package tmp\n%s\n", block)
		file, err := parser.ParseFile(
			token.NewFileSet(), "",
			buf.Bytes(), parser.ParseComments)
		if err != nil {
			return decls, err
		}
		decls = append(decls, file.Decls...)
		buf.Reset()
	}
	return decls, nil
}

func parseBlock(s string) (*ast.BlockStmt, error) {
	var buf bytes.Buffer

	fmt.Fprintf(&buf, "package tmp\nfunc _block() {\n%s\n}", s)
	file, err := parser.ParseFile(token.NewFileSet(), "", buf.Bytes(), parser.ParseComments)
	if err != nil {
		return nil, err
	}
	for _, decl := range file.Decls {
		if decl, ok := decl.(*ast.FuncDecl); ok {
			return decl.Body, nil
		}
	}
	return nil, fmt.Errorf("parse error: no function found in %q", buf.Bytes())
}

// ExprString converts an ast.Expr to the Go source it represents.
func ExprString(expr ast.Expr) string {
	var buf bytes.Buffer
	fs := token.NewFileSet()
	printer.Fprint(&buf, fs, expr)
	return buf.String()
}

// TagKey gets the struct tag item with the
// given key.
func TagKey(field *ast.Field, key string) string {
	if field.Tag != nil {
		return ""
	}
	return reflect.StructTag(field.Tag.Value).Get(key)
}

// FormattedSource converts an abstract syntax tree to
// formatted Go source code.
func FormattedSource(file *ast.File) ([]byte, error) {
	var buf bytes.Buffer

	fileset := token.NewFileSet()

	// our *ast.File did not come from a real Go source
	// file. As such, all of its node positions are 0, and
	// the go/printer package will print the package
	// comment between the package statement and
	// the package name. The most straightforward way
	// to work around this is to put the package comment
	// there ourselves.
	if file.Doc != nil {
		for _, v := range file.Doc.List {
			io.WriteString(&buf, v.Text)
			io.WriteString(&buf, "\n")
		}
		file.Doc = nil
	}
	if err := format.Node(&buf, fileset, file); err != nil {
		return nil, err
	}
	out, err := imports.Process("", buf.Bytes(), nil)
	if err != nil {
		return nil, fmt.Errorf("%v in %s", err, buf.String())
	}
	return out, nil
}
<commit_msg>Add gen.Snippets function for generating snippets of Go code<commit_after>// Package gen provides functions for generating go source code
//
// The gen package provides wrapper functions around the go/ast and
// go/token packages to reduce boilerplate.
package gen

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"reflect"
	"strconv"
	"strings"
	"text/template"

	"golang.org/x/tools/imports"
)

var genFuncMap = template.FuncMap{
	"title":    strings.Title,
	"split":    strings.Split,
	"join":     strings.Join,
	"sanitize": Sanitize,
}

// TypeDecl generates a type declaration with the given name.
func TypeDecl(name *ast.Ident, typ ast.Expr) *ast.GenDecl {
	return &ast.GenDecl{
		Tok: token.TYPE,
		Specs: []ast.Spec{
			&ast.TypeSpec{
				Name: name,
				Type: typ,
			},
		},
	}
}

// Sanitize modifies any names that are reserved in
// Go, so that they may be used as identifiers without
// causing a syntax error.
func Sanitize(name string) string {
	switch name {
	case "break", "default", "func", "interface", "select",
		"case", "defer", "go", "map", "struct",
		"chan", "else", "goto", "package", "switch",
		"const", "fallthrough", "if", "range", "type",
		"continue", "for", "import", "return", "var":
		return name + "_"
	}
	return name
}

// ToString converts the expression to a Go source string.
func ToString(expr ast.Expr) (string, error) {
	var buf bytes.Buffer
	err := format.Node(&buf, token.NewFileSet(), expr)
	return buf.String(), err
}

// Struct creates a struct{} expression. The arguments are a series
// of name/type/tag tuples. Name must be of type *ast.Ident, type
// must be of type ast.Expr, and tag must be of type *ast.BasicLit,
// The number of arguments must be a multiple of 3, or a run-time
// panic will occur.
func Struct(args ...ast.Expr) *ast.StructType {
	fields := new(ast.FieldList)
	if len(args)%3 != 0 {
		panic("Number of args to FieldList must be a multiple of 3, got " + strconv.Itoa(len(args)))
	}
	for i := 0; i < len(args); i += 3 {
		var field ast.Field
		name, typ, tag := args[i], args[i+1], args[i+2]
		if name != nil {
			field.Names = []*ast.Ident{name.(*ast.Ident)}
		}
		if typ != nil {
			field.Type = typ
		}
		if tag != nil {
			field.Tag = tag.(*ast.BasicLit)
		}
		fields.List = append(fields.List, &field)
	}
	return &ast.StructType{Fields: fields}
}

// FieldList generates a field list from strings in the form "[name]
// expr".
func FieldList(fields ...string) (*ast.FieldList, error) {
	result := &ast.FieldList{List: []*ast.Field{}}
	for _, s := range fields {
		parts := strings.SplitN(s, " ", 2)
		if len(parts) == 0 {
			return nil, fmt.Errorf("empty field list item %q", s)
		}
		var names []*ast.Ident
		typeExpr, err := parser.ParseExpr(parts[len(parts)-1])
		if err != nil {
			return nil, fmt.Errorf("could not parse type in %q: %v", s, err)
		}
		if len(parts) > 1 {
			names = []*ast.Ident{ast.NewIdent(parts[0])}
		}
		result.List = append(result.List, &ast.Field{
			Names: names,
			Type:  typeExpr,
		})
	}
	return result, nil
}

// String generates a literal string. If the string contains a double
// quote, backticks are used for quoting instead.
func String(s string) *ast.BasicLit {
	if strings.Contains(s, "\"") && !strings.Contains(s, "`") {
		return &ast.BasicLit{Kind: token.STRING, Value: "`" + s + "`"}
	}
	return &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(s)}
}

// Public turns a string into a public (uppercase)
// identifier.
func Public(name string) *ast.Ident {
	return ast.NewIdent(strings.Title(name))
}

func constDecl(kind token.Token, args ...string) *ast.GenDecl {
	decl := ast.GenDecl{Tok: token.CONST}

	if len(args)%3 != 0 {
		panic("Number of values passed to ConstString must be a multiple of 3")
	}
	for i := 0; i < len(args); i += 3 {
		name, typ, val := args[i], args[i+1], args[i+2]
		lit := &ast.BasicLit{Kind: kind}
		if kind == token.STRING {
			lit.Value = strconv.Quote(val)
		} else {
			lit.Value = val
		}
		a := &ast.ValueSpec{
			Names:  []*ast.Ident{ast.NewIdent(name)},
			Values: []ast.Expr{lit},
		}
		if typ != "" {
			a.Type = ast.NewIdent(typ)
		}
		decl.Specs = append(decl.Specs, a)
	}

	if len(decl.Specs) > 1 {
		decl.Lparen = 1
	}

	return &decl
}

// SimpleType creates an identifier suitable
// for use as a type expression.
func SimpleType(name string) ast.Expr {
	return ast.NewIdent(name)
}

// ConstInt creates a series of numeric const declarations from
// the name/value pairs in args.
func ConstInt(args ...string) *ast.GenDecl {
	return constDecl(token.INT, args...)
}

// ConstString creates a series of string const declarations from
// the name/value pairs in args.
func ConstString(args ...string) *ast.GenDecl {
	return constDecl(token.STRING, args...)
}

// ConstFloat creates a series of floating-point const
// declarations from the name/value pairs in args.
func ConstFloat(args ...string) *ast.GenDecl {
	return constDecl(token.FLOAT, args...)
}

// ConstChar creates a series of character const
// declarations from the name/value pairs in args.
func ConstChar(args ...string) *ast.GenDecl {
	return constDecl(token.CHAR, args...)
}

// ConstImaginary creates a series of imaginary const
// declarations from the name/value pairs in args.
func ConstImaginary(args ...string) *ast.GenDecl {
	return constDecl(token.IMAG, args...)
}

// PackageDoc inserts package-level comments into a file,
// preceding the "package" statement.
func PackageDoc(file *ast.File, comments ...string) *ast.File {
	if len(comments) == 0 {
		return file
	}
	file.Doc = CommentGroup(comments...)
	return file
}

// CommentGroup creates a comment group from strings.
func CommentGroup(comments ...string) *ast.CommentGroup {
	var group ast.CommentGroup
	for _, v := range comments {
		line := bufio.NewScanner(strings.NewReader(v))
		for line.Scan() {
			group.List = append(group.List, &ast.Comment{
				Text: "// " + strings.TrimSpace(line.Text()),
			})
		}
	}
	return &group
}

type Function struct {
	name, receiver, godoc string
	args, returns         []string
	err                   error
	body                  string
}

// Name returns the name of the function.
func (fn *Function) Name() string {
	return fn.name
}

func Func(name string) *Function {
	return &Function{name: name}
}

// Decl generates Go source for a Func.  an error is returned if the
// body, or parameters cannot be parsed.
func (fn *Function) Decl() (*ast.FuncDecl, error) {
	var err error
	var comments *ast.CommentGroup

	if fn.err != nil {
		return nil, fn.err
	}
	if fn.name == "" {
		return nil, errors.New("function name unset")
	}
	if len(fn.body) == 0 {
		return nil, fmt.Errorf("function body for %s unset")
	}

	if fn.godoc != "" {
		comments = &ast.CommentGroup{List: []*ast.Comment{}}
		for _, line := range strings.Split(fn.godoc, "\n") {
			comments.List = append(comments.List, &ast.Comment{
				Text: "// " + line + "\n",
			})
		}
	}
	fl := func(args ...string) (list *ast.FieldList) {
		if len(args) == 0 || len(args[0]) == 0 || err != nil {
			return nil
		}
		list, err = FieldList(args...)
		return list
	}
	args := fl(fn.args...)
	returns := fl(fn.returns...)
	receiver := fl(fn.receiver)
	if err != nil {
		return nil, err
	}
	body, err := parseBlock(fn.body)
	if err != nil {
		return nil, fmt.Errorf("could not parse function body of %s: %v in\n%s", fn.name, err, fn.body)
	}
	return &ast.FuncDecl{
		Doc:  comments,
		Recv: receiver,
		Name: ast.NewIdent(fn.name),
		Type: &ast.FuncType{
			Params:  args,
			Results: returns,
		},
		Body: body,
	}, nil
}

// Body sets the body of a function. The body should not include
// enclosing braces.
func (fn *Function) Body(format string, v ...interface{}) *Function {
	fn.body = fmt.Sprintf(format, v...)
	return fn
}

// BodyTmpl allows use of the text/template package to construct
// the body of a function.
func (fn *Function) BodyTmpl(tmpl string, dot interface{}) *Function {
	var buf bytes.Buffer
	t, err := template.New(fn.Name()).Funcs(genFuncMap).Parse(tmpl)
	if err != nil {
		fn.err = err
	} else if err := t.Execute(&buf, dot); err != nil {
		fn.err = err
	} else {
		fn.body = buf.String()
	}
	return fn
}

// Returns sets the return values of a function. Each return
// value should be a string matching the Go syntax for a
// single return value.
func (fn *Function) Returns(values ...string) *Function {
	fn.returns = values
	return fn
}

// Comments sets the Godoc comments for the function.
func (fn *Function) Comment(s string) *Function {
	fn.godoc = s
	return fn
}

// Args sets the arguments that a function takes.
func (fn *Function) Args(args ...string) *Function {
	fn.args = args
	return fn
}

// Receiver turns the function into a method operating on
// the specified type.
func (fn *Function) Receiver(receiver string) *Function {
	fn.receiver = receiver
	return fn
}

// Declarations parses a list of Go source code blocks and converts
// them into *ast.Decl values. If a parsing error occurs, it is returned
// immediately and no further parsing takes place.
func Declarations(blocks ...string) ([]ast.Decl, error) {
	var buf bytes.Buffer
	decls := make([]ast.Decl, 0, len(blocks))
	for _, block := range blocks {
		fmt.Fprintf(&buf, "package tmp\n%s\n", block)
		file, err := parser.ParseFile(
			token.NewFileSet(), "",
			buf.Bytes(), parser.ParseComments)
		if err != nil {
			return decls, err
		}
		decls = append(decls, file.Decls...)
		buf.Reset()
	}
	return decls, nil
}

// Snippets evaluates zero or more input templates with dot set to val
// and parses them as Go source code.
func Snippets(val interface{}, snippets ...string) ([]ast.Decl, error) {
	var buf bytes.Buffer
	blocks := make([]string, 0, len(snippets))

	for _, text := range snippets {
		t, err := template.New("snippet").Funcs(genFuncMap).Parse(text)
		if err != nil {
			return nil, err
		} else if err := t.Execute(&buf, val); err != nil {
			return nil, err
		}
		blocks = append(blocks, buf.String())
		buf.Reset()
	}
	return Declarations(blocks...)
}

func parseBlock(s string) (*ast.BlockStmt, error) {
	var buf bytes.Buffer

	fmt.Fprintf(&buf, "package tmp\nfunc _block() {\n%s\n}", s)
	file, err := parser.ParseFile(token.NewFileSet(), "", buf.Bytes(), parser.ParseComments)
	if err != nil {
		return nil, err
	}
	for _, decl := range file.Decls {
		if decl, ok := decl.(*ast.FuncDecl); ok {
			return decl.Body, nil
		}
	}
	return nil, fmt.Errorf("parse error: no function found in %q", buf.Bytes())
}

// ExprString converts an ast.Expr to the Go source it represents.
func ExprString(expr ast.Expr) string {
	var buf bytes.Buffer
	fs := token.NewFileSet()
	printer.Fprint(&buf, fs, expr)
	return buf.String()
}

// TagKey gets the struct tag item with the
// given key.
func TagKey(field *ast.Field, key string) string {
	if field.Tag != nil {
		return ""
	}
	return reflect.StructTag(field.Tag.Value).Get(key)
}

// FormattedSource converts an abstract syntax tree to
// formatted Go source code.
func FormattedSource(file *ast.File) ([]byte, error) {
	var buf bytes.Buffer

	fileset := token.NewFileSet()

	// our *ast.File did not come from a real Go source
	// file. As such, all of its node positions are 0, and
	// the go/printer package will print the package
	// comment between the package statement and
	// the package name. The most straightforward way
	// to work around this is to put the package comment
	// there ourselves.
	if file.Doc != nil {
		for _, v := range file.Doc.List {
			io.WriteString(&buf, v.Text)
			io.WriteString(&buf, "\n")
		}
		file.Doc = nil
	}
	if err := format.Node(&buf, fileset, file); err != nil {
		return nil, err
	}
	out, err := imports.Process("", buf.Bytes(), nil)
	if err != nil {
		return nil, fmt.Errorf("%v in %s", err, buf.String())
	}
	return out, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/pilu/traffic"
	"github.com/spf13/cast"
	"github.com/spf13/hugo/parser"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/taskqueue"
)

func LongformQueueHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r.Request)
	t := taskqueue.NewPOSTTask("/longform/work", url.Values{})
	_, err := taskqueue.Add(c, t, "")

	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		fmt.Fprint(w, "success.\n")
	}
}

func LongformWorkHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r.Request)

	// Read drafts from disk
	dir := "./longform/drafts/"
	drafts, err := ioutil.ReadDir(dir)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// Iterate through
	for _, file := range drafts {
		if strings.HasPrefix(file.Name(), "20") && file.Mode().IsRegular() {
			f, err := os.Open(dir + file.Name())
			defer f.Close()
			if err != nil {
				log.Errorf(c, "Error reading file: %v", err.Error())
				http.Error(w, err.Error(), 500)
				return
			}
			// get the page from file
			p, err := parser.ReadFrom(f)
			if err != nil {
				log.Errorf(c, "Error reading file: %v", err.Error())
				http.Error(w, err.Error(), 500)
				return
			}
			meta_uncast, err := p.Metadata()
			if err != nil {
				log.Errorf(c, "Error reading file: %v", err.Error())
				http.Error(w, err.Error(), 500)
				return
			}

			meta, err := cast.ToStringMapStringE(meta_uncast)
			if err != nil {
				log.Errorf(c, "Error reading file: %v", err.Error())
				http.Error(w, err.Error(), 500)
				return
			}

			// create entries for those that don't exist, update those that do
			entry, err := GetLongform(c, file.Name())
			if entry == nil {
				// TODO: datetime
				log.Debugf(c, "Meta: %+v", meta)
				posted_at := time.Now()
				datetime := time.Date(posted_at.Year(), posted_at.Month(), posted_at.Day(), posted_at.Hour(), posted_at.Minute(), 0, 0, time.UTC)
				entry = NewEntry(meta["title"], string(p.Content()), datetime, true, []string{})
			}
			entry.Draft = true
			entry.Save(c)
		}
	}

	// Read posts from disk,
	dir = "./longform/posts/"
	posts, err := ioutil.ReadDir(dir)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// create entries for those that don't exist, update those that do
	for _, file := range posts {
		if strings.HasPrefix(file.Name(), "20") && file.Mode().IsRegular() {
			log.Infof(c, "Post: %+v", file.Name())
		}
	}
}
<commit_msg>set dates<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/pilu/traffic"
	"github.com/spf13/cast"
	"github.com/spf13/hugo/parser"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"
	"google.golang.org/appengine/taskqueue"
)

func LongformQueueHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r.Request)
	t := taskqueue.NewPOSTTask("/longform/work", url.Values{})
	_, err := taskqueue.Add(c, t, "")

	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		fmt.Fprint(w, "success.\n")
	}
}

func LongformWorkHandler(w traffic.ResponseWriter, r *traffic.Request) {
	c := appengine.NewContext(r.Request)

	// Read drafts from disk
	dir := "./longform/drafts/"
	drafts, err := ioutil.ReadDir(dir)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// Iterate through
	for _, file := range drafts {
		if strings.HasPrefix(file.Name(), "20") && file.Mode().IsRegular() {
			f, err := os.Open(dir + file.Name())
			defer f.Close()
			if err != nil {
				log.Errorf(c, "Error reading file: %v", err.Error())
				http.Error(w, err.Error(), 500)
				return
			}
			// get the page from file
			p, err := parser.ReadFrom(f)
			if err != nil {
				log.Errorf(c, "Error reading file: %v", err.Error())
				http.Error(w, err.Error(), 500)
				return
			}
			meta_uncast, err := p.Metadata()
			if err != nil {
				log.Errorf(c, "Error reading file: %v", err.Error())
				http.Error(w, err.Error(), 500)
				return
			}

			meta, err := cast.ToStringMapStringE(meta_uncast)
			if err != nil {
				log.Errorf(c, "Error reading file: %v", err.Error())
				http.Error(w, err.Error(), 500)
				return
			}

			// create entries for those that don't exist, update those that do
			entry, err := GetLongform(c, file.Name())
			if err != nil {
				log.Warningf(c, "Error getting longform %v: %v", file.Name(), err.Error())
			}

			if entry == nil {
				log.Debugf(c, "Meta: %+v", meta)
				now := time.Now()

				year := now.Year()
				mnth := now.Month()
				day := now.Day()
				hour := now.Hour()
				min := now.Minute()

				// TODO: Don't throw away errors
				if meta["time"] != "" {
					split := strings.Split(meta["time"], ":")
					hour, _ = strconv.Atoi(split[0])
					min, _ = strconv.Atoi(split[1])
				}

				split := strings.Split(file.Name(), "-")
				year, _ = strconv.Atoi(split[0])
				m, _ := strconv.Atoi(split[1])
				mnth = time.Month(m)
				day, _ = strconv.Atoi(split[2])

				datetime := time.Date(year, mnth, day, hour, min, 0, 0, time.UTC)
				entry = NewEntry(meta["title"], string(p.Content()), datetime, true, []string{})
			}
			entry.Draft = true
			entry.Longform = file.Name()
			entry.Save(c)
		}
	}

	// Read posts from disk,
	dir = "./longform/posts/"
	posts, err := ioutil.ReadDir(dir)
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// create entries for those that don't exist, update those that do
	for _, file := range posts {
		if strings.HasPrefix(file.Name(), "20") && file.Mode().IsRegular() {
			log.Infof(c, "Post: %+v", file.Name())
		}
	}
}
<|endoftext|>


<commit_before>package winrm

import (
	"bytes"
	"errors"
	"io"
	"strings"
)

type commandWriter struct {
	*Command
	eof bool
}

type commandReader struct {
	*Command
	write  *io.PipeWriter
	read   *io.PipeReader
	stream string
}

// Command represents a given command running on a Shell. This structure allows to get access
// to the various stdout, stderr and stdin pipes.
type Command struct {
	client   *Client
	shell    *Shell
	id       string
	exitCode int
	finished bool
	err      error

	Stdin  *commandWriter
	Stdout *commandReader
	Stderr *commandReader

	done   chan struct{}
	cancel chan struct{}
}

func newCommand(shell *Shell, ids string) *Command {
	command := &Command{
		shell:    shell,
		client:   shell.client,
		id:       ids,
		exitCode: 0,
		err:      nil,
		done:     make(chan struct{}),
		cancel:   make(chan struct{}),
	}

	command.Stdout = newCommandReader("stdout", command)
	command.Stdin = &commandWriter{
		Command: command,
		eof:     false,
	}
	command.Stderr = newCommandReader("stderr", command)

	go fetchOutput(command)

	return command
}

func newCommandReader(stream string, command *Command) *commandReader {
	read, write := io.Pipe()
	return &commandReader{
		Command: command,
		stream:  stream,
		write:   write,
		read:    read,
	}
}

func fetchOutput(command *Command) {
	for {
		select {
		case <-command.cancel:
			close(command.done)
			return
		default:
			finished, err := command.slurpAllOutput()
			if finished {
				command.err = err
				close(command.done)
				return
			}
		}
	}
}

func (c *Command) check() error {
	if c.id == "" {
		return errors.New("Command has already been closed")
	}
	if c.shell == nil {
		return errors.New("Command has no associated shell")
	}
	if c.client == nil {
		return errors.New("Command has no associated client")
	}
	return nil
}

// Close will terminate the running command
func (c *Command) Close() error {
	if err := c.check(); err != nil {
		return err
	}

	select { // close cancel channel if it's still open
	case <-c.cancel:
	default:
		close(c.cancel)
	}

	request := NewSignalRequest(c.client.url, c.shell.id, c.id, &c.client.Parameters)
	defer request.Free()

	_, err := c.client.sendRequest(request)
	return err
}

func (c *Command) slurpAllOutput() (bool, error) {
	if err := c.check(); err != nil {
		c.Stderr.write.CloseWithError(err)
		c.Stdout.write.CloseWithError(err)
		return true, err
	}

	request := NewGetOutputRequest(c.client.url, c.shell.id, c.id, "stdout stderr", &c.client.Parameters)
	defer request.Free()

	response, err := c.client.sendRequest(request)
	if err != nil {
		if strings.Contains(err.Error(), "OperationTimeout") {
			// Operation timeout because there was no command output
			return false, err
		}
		if strings.Contains(err.Error(), "EOF") {
			c.exitCode = 16001
		}

		c.Stderr.write.CloseWithError(err)
		c.Stdout.write.CloseWithError(err)
		return true, err
	}

	var exitCode int
	var stdout, stderr bytes.Buffer
	finished, exitCode, err := ParseSlurpOutputErrResponse(response, &stdout, &stderr)
	if err != nil {
		c.Stderr.write.CloseWithError(err)
		c.Stdout.write.CloseWithError(err)
		return true, err
	}
	if stdout.Len() > 0 {
		c.Stdout.write.Write(stdout.Bytes())
	}
	if stderr.Len() > 0 {
		c.Stderr.write.Write(stderr.Bytes())
	}
	if finished {
		c.exitCode = exitCode
		c.Stderr.write.Close()
		c.Stdout.write.Close()
	}

	return finished, nil
}

func (c *Command) sendInput(data []byte) error {
	if err := c.check(); err != nil {
		return err
	}

	request := NewSendInputRequest(c.client.url, c.shell.id, c.id, data, &c.client.Parameters)
	defer request.Free()

	_, err := c.client.sendRequest(request)
	return err
}

// ExitCode returns command exit code when it is finished. Before that the result is always 0.
func (c *Command) ExitCode() int {
	return c.exitCode
}

// Wait function will block the current goroutine until the remote command terminates.
func (c *Command) Wait() {
	// block until finished
	<-c.done
}

// Write data to this Pipe
// commandWriter implements io.Writer interface
func (w *commandWriter) Write(data []byte) (int, error) {

	var (
		written int
		err     error
	)

	for len(data) > 0 {
		if w.eof {
			return written, io.EOF
		}
		// never send more data than our EnvelopeSize.
		n := min(w.client.Parameters.EnvelopeSize-1000, len(data))
		if err := w.sendInput(data[:n]); err != nil {
			break
		}
		data = data[n:]
		written += n
	}

	return written, err
}

func min(a int, b int) int {
	if a < b {
		return a
	}
	return b
}

// Close method wrapper
// commandWriter implements io.Closer interface
func (w *commandWriter) Close() error {
	w.eof = true
	return w.Close()
}

// Read data from this Pipe
func (r *commandReader) Read(buf []byte) (int, error) {
	n, err := r.read.Read(buf)
	if err != nil && err != io.EOF {
		return 0, err
	}
	return n, err
}
<commit_msg>Avoid infinite loop on command Stdin.Close()<commit_after>package winrm

import (
	"bytes"
	"errors"
	"io"
	"strings"
)

type commandWriter struct {
	*Command
	eof bool
}

type commandReader struct {
	*Command
	write  *io.PipeWriter
	read   *io.PipeReader
	stream string
}

// Command represents a given command running on a Shell. This structure allows to get access
// to the various stdout, stderr and stdin pipes.
type Command struct {
	client   *Client
	shell    *Shell
	id       string
	exitCode int
	finished bool
	err      error

	Stdin  *commandWriter
	Stdout *commandReader
	Stderr *commandReader

	done   chan struct{}
	cancel chan struct{}
}

func newCommand(shell *Shell, ids string) *Command {
	command := &Command{
		shell:    shell,
		client:   shell.client,
		id:       ids,
		exitCode: 0,
		err:      nil,
		done:     make(chan struct{}),
		cancel:   make(chan struct{}),
	}

	command.Stdout = newCommandReader("stdout", command)
	command.Stdin = &commandWriter{
		Command: command,
		eof:     false,
	}
	command.Stderr = newCommandReader("stderr", command)

	go fetchOutput(command)

	return command
}

func newCommandReader(stream string, command *Command) *commandReader {
	read, write := io.Pipe()
	return &commandReader{
		Command: command,
		stream:  stream,
		write:   write,
		read:    read,
	}
}

func fetchOutput(command *Command) {
	for {
		select {
		case <-command.cancel:
			close(command.done)
			return
		default:
			finished, err := command.slurpAllOutput()
			if finished {
				command.err = err
				close(command.done)
				return
			}
		}
	}
}

func (c *Command) check() error {
	if c.id == "" {
		return errors.New("Command has already been closed")
	}
	if c.shell == nil {
		return errors.New("Command has no associated shell")
	}
	if c.client == nil {
		return errors.New("Command has no associated client")
	}
	return nil
}

// Close will terminate the running command
func (c *Command) Close() error {
	if err := c.check(); err != nil {
		return err
	}

	select { // close cancel channel if it's still open
	case <-c.cancel:
	default:
		close(c.cancel)
	}

	request := NewSignalRequest(c.client.url, c.shell.id, c.id, &c.client.Parameters)
	defer request.Free()

	_, err := c.client.sendRequest(request)
	return err
}

func (c *Command) slurpAllOutput() (bool, error) {
	if err := c.check(); err != nil {
		c.Stderr.write.CloseWithError(err)
		c.Stdout.write.CloseWithError(err)
		return true, err
	}

	request := NewGetOutputRequest(c.client.url, c.shell.id, c.id, "stdout stderr", &c.client.Parameters)
	defer request.Free()

	response, err := c.client.sendRequest(request)
	if err != nil {
		if strings.Contains(err.Error(), "OperationTimeout") {
			// Operation timeout because there was no command output
			return false, err
		}
		if strings.Contains(err.Error(), "EOF") {
			c.exitCode = 16001
		}

		c.Stderr.write.CloseWithError(err)
		c.Stdout.write.CloseWithError(err)
		return true, err
	}

	var exitCode int
	var stdout, stderr bytes.Buffer
	finished, exitCode, err := ParseSlurpOutputErrResponse(response, &stdout, &stderr)
	if err != nil {
		c.Stderr.write.CloseWithError(err)
		c.Stdout.write.CloseWithError(err)
		return true, err
	}
	if stdout.Len() > 0 {
		c.Stdout.write.Write(stdout.Bytes())
	}
	if stderr.Len() > 0 {
		c.Stderr.write.Write(stderr.Bytes())
	}
	if finished {
		c.exitCode = exitCode
		c.Stderr.write.Close()
		c.Stdout.write.Close()
	}

	return finished, nil
}

func (c *Command) sendInput(data []byte) error {
	if err := c.check(); err != nil {
		return err
	}

	request := NewSendInputRequest(c.client.url, c.shell.id, c.id, data, &c.client.Parameters)
	defer request.Free()

	_, err := c.client.sendRequest(request)
	return err
}

// ExitCode returns command exit code when it is finished. Before that the result is always 0.
func (c *Command) ExitCode() int {
	return c.exitCode
}

// Wait function will block the current goroutine until the remote command terminates.
func (c *Command) Wait() {
	// block until finished
	<-c.done
}

// Write data to this Pipe
// commandWriter implements io.Writer interface
func (w *commandWriter) Write(data []byte) (int, error) {

	var (
		written int
		err     error
	)

	for len(data) > 0 {
		if w.eof {
			return written, io.EOF
		}
		// never send more data than our EnvelopeSize.
		n := min(w.client.Parameters.EnvelopeSize-1000, len(data))
		if err := w.sendInput(data[:n]); err != nil {
			break
		}
		data = data[n:]
		written += n
	}

	return written, err
}

func min(a int, b int) int {
	if a < b {
		return a
	}
	return b
}

// Close method wrapper
// commandWriter implements io.Closer interface
func (w *commandWriter) Close() error {
	w.eof = true
        return nil
}

// Read data from this Pipe
func (r *commandReader) Read(buf []byte) (int, error) {
	n, err := r.read.Read(buf)
	if err != nil && err != io.EOF {
		return 0, err
	}
	return n, err
}
<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

// +build gofuzz

package vm

import (
	"bytes"
	"flag"
)

func Fuzz(data []byte) int {
	// We need to successfully parse flags to initialize the glog logger used
	// by the compiler, but the fuzzer gets called with flags captured by the
	// libfuzzer main, which we don't want to intercept here.
	flag.Commandline = flag.NewFlagSet("", flag.ContinueOnError)
	flag.Parse()
	if _, err := Compile("fuzz", bytes.NewReader(data), false, false, false, nil); err != nil {
		return 0
	}
	return 1
}
<commit_msg>Double-ugh.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

// +build gofuzz

package vm

import (
	"bytes"
	"flag"
)

func Fuzz(data []byte) int {
	// We need to successfully parse flags to initialize the glog logger used
	// by the compiler, but the fuzzer gets called with flags captured by the
	// libfuzzer main, which we don't want to intercept here.
	flag.CommandLine = flag.NewFlagSet("", flag.ContinueOnError)
	flag.Parse()
	if _, err := Compile("fuzz", bytes.NewReader(data), false, false, false, nil); err != nil {
		return 0
	}
	return 1
}
<|endoftext|>


<commit_before>package core

import "fmt"

type Domain interface {
	Subdomain(string) Domain

	Subscribe(string, uint, []interface{}) error
	Register(string, uint, []interface{}) error
	Publish(string, uint, []interface{}) error
	Call(string, uint, []interface{}) error

	Unsubscribe(string) error
	Unregister(string) error

	Join(Connection) error
	Leave() error
	GetApp() App
}

type domain struct {
	app           *app
	name          string
	joined        bool
	subscriptions map[uint]*boundEndpoint
	registrations map[uint]*boundEndpoint
}

type boundEndpoint struct {
	callback      uint
	endpoint      string
	expectedTypes []interface{}
}

// Create a new domain. If no superdomain is provided, creates an app as well
// If the app exists, has a connection, and is connected then immediately call onJoin on that domain
func NewDomain(name string, a *app) Domain {
	Debug("Creating domain %s", name)

	if a == nil {
		a = &app{
			domains:    make([]*domain, 0),
			serializer: new(jSONSerializer),
			in:         make(chan message, 10),
			up:         make(chan Callback, 10),
			listeners:  make(map[uint]chan message),
		}
	}

	d := &domain{
		app:           a,
		name:          name,
		joined:        false,
		subscriptions: make(map[uint]*boundEndpoint),
		registrations: make(map[uint]*boundEndpoint),
	}

	// TODO: trigger onJoin if the superdomain has joined

	a.domains = append(a.domains, d)
	return d
}

func (d domain) Subdomain(name string) Domain {
	Debug("Creating subdomain %s", name)
	return NewDomain(d.name+"."+name, d.app)
}

func (d domain) GetApp() App {
	return d.app
}

// Accepts a connection that has just been opened. This method should only
// be called once, to initialize the fabric
func (c domain) Join(conn Connection) error {
	if c.joined {
		return fmt.Errorf("Domain %s is already joined", c.name)
	}

	// Handshake between the connection and the app
	c.app.Connection = conn
	conn.SetApp(c.app)

	// Set the agent string, or who WE are. When this domain leaves, termintate the connection
	c.app.agent = c.name

	// Should we hard close on conn.Close()? The App may be interested in knowing about the close
	if err := c.app.Send(&hello{Realm: c.name, Details: map[string]interface{}{}}); err != nil {
		c.app.Close("ERR: could not send a hello message")
		return err
	}

	if msg, err := c.app.getMessageTimeout(); err != nil {
		c.app.Close(err.Error())
		return err
	} else if _, ok := msg.(*welcome); !ok {
		c.app.Send(&abort{Details: map[string]interface{}{}, Reason: "Error- unexpected_message_type"})
		c.app.Close("Error- unexpected_message_type")
		return fmt.Errorf(formatUnexpectedMessage(msg, wELCOME.String()))
	}

	// This is super dumb, and the reason its in here was fixed. Please revert
	go c.app.receiveLoop()

	// old contents of app.join
	for _, x := range c.app.domains {
		if !x.joined {
			x.joined = true
			// x.Delegate.OnJoin(x.name)
			// Invoke the onjoin method for the domain!
		}
	}

	Info("Domain joined")
	return nil
}

func (c *domain) Leave() error {
	for _, v := range c.registrations {
		c.Unregister(v.endpoint)
	}

	for _, v := range c.subscriptions {
		c.Unsubscribe(v.endpoint)
	}

	if dems, ok := removeDomain(c.app.domains, c); !ok {
		return fmt.Errorf("WARN: couldn't find %s to remove!", c)
	} else {
		c.app.domains = dems
	}

	// if no domains remain, terminate the connection
	if len(c.app.domains) == 0 || c.app.agent == c.name {
		c.app.Close("No domains connected")
	}

	// Trigger closing callbacks

	return nil
}

/////////////////////////////////////////////
// Message Patterns
/////////////////////////////////////////////

// Apply the arguments to the given function on this domain.
// Accepts a list of arguments and a list of types for cuminication
// func (c domain) apply(fn func(string, uint, []interface{}), endpoint string, cb uint, eb uint) {
// 	// TODO: Validate endpoint, else errback
// 	endpoint = makeEndpoint(c.name, endpoint)

// 	// with function Subscribe:
// 	if e := fn(&c, endpoint, cb); e != nil {
// 		// If the function returns an error, err it back immediately
// 	}

// 	// Note that the above won't work for unsubscribe and unregister, since their success case returns nil
// }

func (c domain) Subscribe(endpoint string, requestId uint, types []interface{}) error {
	endpoint = makeEndpoint(c.name, endpoint)
	sub := &subscribe{Request: requestId, Options: make(map[string]interface{}), Name: endpoint}

	if msg, err := c.app.requestListenType(sub, "*core.subscribed"); err != nil {
		return err
	} else {
		subbed := msg.(*subscribed)
		Info("Subscribed: %s", endpoint)
		c.subscriptions[subbed.Subscription] = &boundEndpoint{requestId, endpoint, types}
		return nil
	}
}

func (c domain) Register(endpoint string, requestId uint, types []interface{}) error {
	endpoint = makeEndpoint(c.name, endpoint)
	register := &register{Request: requestId, Options: make(map[string]interface{}), Name: endpoint}

	if msg, err := c.app.requestListenType(register, "*core.registered"); err != nil {
		return err
	} else {
		Info("Registered: %s", endpoint)
		reg := msg.(*registered)
		c.registrations[reg.Registration] = &boundEndpoint{requestId, endpoint, types}
		return nil
	}
}

func (c domain) Publish(endpoint string, requestId uint, args []interface{}) error {
	return c.app.Send(&publish{
		Request:   NewID(),
		Options:   make(map[string]interface{}),
		Name:      makeEndpoint(c.name, endpoint),
		Arguments: args,
	})
}

func (c domain) Call(endpoint string, requestId uint, args []interface{}) error {
	endpoint = makeEndpoint(c.name, endpoint)
	call := &call{Request: requestId, Name: endpoint, Options: make(map[string]interface{}), Arguments: args}

	if msg, err := c.app.requestListenType(call, "*core.result"); err != nil {
		return err
	} else {
		c.app.CallbackSend(requestId, msg.(*result).Arguments...)
		return nil
	}
}

func (c domain) Unsubscribe(endpoint string) error {
	endpoint = makeEndpoint(c.name, endpoint)

	if id, _, ok := bindingForEndpoint(c.subscriptions, endpoint); !ok {
		return fmt.Errorf("domain %s is not registered with this client.", endpoint)
	} else {
		sub := &unsubscribe{Request: NewID(), Subscription: id}

		if _, err := c.app.requestListenType(sub, "*core.unsubscribed"); err != nil {
			return err
		} else {
			Info("Unsubscribed: %s", endpoint)
			delete(c.subscriptions, id)
			return nil
		}
	}
}

func (c domain) Unregister(endpoint string) error {
	endpoint = makeEndpoint(c.name, endpoint)

	if id, _, ok := bindingForEndpoint(c.registrations, endpoint); !ok {
		return fmt.Errorf("domain %s is not registered with this domain.", endpoint)
	} else {
		unregister := &unregister{Request: NewID(), Registration: id}

		if _, err := c.app.requestListenType(unregister, "*core.unregistered"); err != nil {
			return err
		} else {
			Info("Unregistered: %s", endpoint)
			delete(c.registrations, id)
			return nil
		}
	}
}

func (c domain) handleInvocation(msg *invocation, binding *boundEndpoint) {
	if err := softCumin(binding.expectedTypes, msg.Arguments); err == nil {
		c.app.CallbackSend(binding.callback, append([]interface{}{msg.Request}, msg.Arguments...)...)
	} else {
		tosend := &errorMessage{
			Type:      iNVOCATION,
			Request:   msg.Request,
			Details:   make(map[string]interface{}),
			Arguments: msg.Arguments,
			Error:     err.Error(),
		}

		if err := c.app.Send(tosend); err != nil {
			Warn("error sending message:", err)
		}
	}
}

func (c *domain) handlePublish(msg *event, binding *boundEndpoint) {
	if e := softCumin(binding.expectedTypes, msg.Arguments); e == nil {
		c.app.CallbackSend(binding.callback, msg.Arguments...)
	} else {
		tosend := &errorMessage{Type: pUBLISH, Request: msg.Subscription, Details: make(map[string]interface{}), Arguments: make([]interface{}, 0), Error: e.Error()}

		if err := c.app.Send(tosend); err != nil {
			Warn("error sending message:", err)
		}
	}
}
<commit_msg>updated xcode config to product output in the local directory<commit_after>package core

import "fmt"

type Domain interface {
	Subdomain(string) Domain

	Subscribe(string, uint, []interface{}) error
	Register(string, uint, []interface{}) error
	Publish(string, uint, []interface{}) error
	Call(string, uint, []interface{}) error

	Unsubscribe(string) error
	Unregister(string) error

	Join(Connection) error
	Leave() error
	GetApp() App
}

type domain struct {
	app           *app
	name          string
	joined        bool
	subscriptions map[uint]*boundEndpoint
	registrations map[uint]*boundEndpoint
}

type boundEndpoint struct {
	callback      uint
	endpoint      string
	expectedTypes []interface{}
}

// Create a new domain. If no superdomain is provided, creates an app as well
// If the app exists, has a connection, and is connected then immediately call onJoin on that domain
func NewDomain(name string, a *app) Domain {
	Debug("Creating domain %s", name)

	if a == nil {
		a = &app{
			domains:    make([]*domain, 0),
			serializer: new(jSONSerializer),
			in:         make(chan message, 10),
			up:         make(chan Callback, 10),
			listeners:  make(map[uint]chan message),
		}
	}

	d := &domain{
		app:           a,
		name:          name,
		joined:        false,
		subscriptions: make(map[uint]*boundEndpoint),
		registrations: make(map[uint]*boundEndpoint),
	}

	// TODO: trigger onJoin if the superdomain has joined

	a.domains = append(a.domains, d)
	return d
}

func (d domain) Subdomain(name string) Domain {
	Debug("Creating subdomain %s", name)
	return NewDomain(d.name+"."+name, d.app)
}

func (d domain) GetApp() App {
	return d.app
}

// Accepts a connection that has just been opened. This method should only
// be called once, to initialize the fabric
func (c domain) Join(conn Connection) error {
	if c.joined {
		return fmt.Errorf("Domain %s is already joined", c.name)
	}

	// Handshake between the connection and the app
	c.app.Connection = conn
	conn.SetApp(c.app)

	// Set the agent string, or who WE are. When this domain leaves, termintate the connection
	c.app.agent = c.name

	// Should we hard close on conn.Close()? The App may be interested in knowing about the close
	if err := c.app.Send(&hello{Realm: c.name, Details: map[string]interface{}{}}); err != nil {
		c.app.Close("ERR: could not send a hello message")
		return err
	}

	if msg, err := c.app.getMessageTimeout(); err != nil {
		c.app.Close(err.Error())
		return err
	} else if _, ok := msg.(*welcome); !ok {
		c.app.Send(&abort{Details: map[string]interface{}{}, Reason: "Error- unexpected_message_type"})
		c.app.Close("Error- unexpected_message_type")
		return fmt.Errorf(formatUnexpectedMessage(msg, wELCOME.String()))
	}

	// This is super dumb, and the reason its in here was fixed. Please revert
	go c.app.receiveLoop()

	// old contents of app.join
	for _, x := range c.app.domains {
		if !x.joined {
			x.joined = true
			// x.Delegate.OnJoin(x.name)
			// Invoke the onjoin method for the domain!
		}
	}

	Info("Domain joined")
	return nil
}

func (c *domain) Leave() error {
	for _, v := range c.registrations {
		c.Unregister(v.endpoint)
	}

	for _, v := range c.subscriptions {
		c.Unsubscribe(v.endpoint)
	}

	if dems, ok := removeDomain(c.app.domains, c); !ok {
		return fmt.Errorf("WARN: couldn't find %s to remove!", c)
	} else {
		c.app.domains = dems
	}

	// if no domains remain, terminate the connection
	if len(c.app.domains) == 0 || c.app.agent == c.name {
		c.app.Close("No domains connected")
	}

	// Trigger closing callbacks

	return nil
}

/////////////////////////////////////////////
// Message Patterns
/////////////////////////////////////////////

// Apply the arguments to the given function on this domain.
// Accepts a list of arguments and a list of types for cuminication
// func (c domain) apply(fn func(string, uint, []interface{}), endpoint string, cb uint, eb uint) {
// 	// TODO: Validate endpoint, else errback
// 	endpoint = makeEndpoint(c.name, endpoint)

// 	// with function Subscribe:
// 	if e := fn(&c, endpoint, cb); e != nil {
// 		// If the function returns an error, err it back immediately
// 	}

// 	// Note that the above won't work for unsubscribe and unregister, since their success case returns nil
// }

func (c domain) Subscribe(endpoint string, requestId uint, types []interface{}) error {
	endpoint = makeEndpoint(c.name, endpoint)
	sub := &subscribe{Request: requestId, Options: make(map[string]interface{}), Name: endpoint}

	if msg, err := c.app.requestListenType(sub, "*core.subscribed"); err != nil {
		return err
	} else {
		subbed := msg.(*subscribed)
		Info("Subscribed: %s", endpoint)
		c.subscriptions[subbed.Subscription] = &boundEndpoint{requestId, endpoint, types}
		return nil
	}
}

func (c domain) Register(endpoint string, requestId uint, types []interface{}) error {
	endpoint = makeEndpoint(c.name, endpoint)
	register := &register{Request: requestId, Options: make(map[string]interface{}), Name: endpoint}

	if msg, err := c.app.requestListenType(register, "*core.registered"); err != nil {
		return err
	} else {
		Info("Registered: %s", endpoint)
		reg := msg.(*registered)
		c.registrations[reg.Registration] = &boundEndpoint{requestId, endpoint, types}
		return nil
	}
}

func (c domain) Publish(endpoint string, requestId uint, args []interface{}) error {
	return c.app.Send(&publish{
		Request:   NewID(),
		Options:   make(map[string]interface{}),
		Name:      makeEndpoint(c.name, endpoint),
		Arguments: args,
	})
}

func (c domain) Call(endpoint string, requestId uint, args []interface{}) error {
	endpoint = makeEndpoint(c.name, endpoint)
	call := &call{Request: requestId, Name: endpoint, Options: make(map[string]interface{}), Arguments: args}

	if msg, err := c.app.requestListenType(call, "*core.result"); err != nil {
		return err
	} else {
		// No cumin here?
		c.app.CallbackSend(requestId, msg.(*result).Arguments...)
		return nil
	}
}

func (c domain) Unsubscribe(endpoint string) error {
	endpoint = makeEndpoint(c.name, endpoint)

	if id, _, ok := bindingForEndpoint(c.subscriptions, endpoint); !ok {
		return fmt.Errorf("domain %s is not registered with this client.", endpoint)
	} else {
		sub := &unsubscribe{Request: NewID(), Subscription: id}

		if _, err := c.app.requestListenType(sub, "*core.unsubscribed"); err != nil {
			return err
		} else {
			Info("Unsubscribed: %s", endpoint)
			delete(c.subscriptions, id)
			return nil
		}
	}
}

func (c domain) Unregister(endpoint string) error {
	endpoint = makeEndpoint(c.name, endpoint)

	if id, _, ok := bindingForEndpoint(c.registrations, endpoint); !ok {
		return fmt.Errorf("domain %s is not registered with this domain.", endpoint)
	} else {
		unregister := &unregister{Request: NewID(), Registration: id}

		if _, err := c.app.requestListenType(unregister, "*core.unregistered"); err != nil {
			return err
		} else {
			Info("Unregistered: %s", endpoint)
			delete(c.registrations, id)
			return nil
		}
	}
}

func (c domain) handleInvocation(msg *invocation, binding *boundEndpoint) {
	if err := softCumin(binding.expectedTypes, msg.Arguments); err == nil {
		c.app.CallbackSend(binding.callback, append([]interface{}{msg.Request}, msg.Arguments...)...)
	} else {
		tosend := &errorMessage{
			Type:      iNVOCATION,
			Request:   msg.Request,
			Details:   make(map[string]interface{}),
			Arguments: msg.Arguments,
			Error:     err.Error(),
		}

		if err := c.app.Send(tosend); err != nil {
			Warn("error sending message:", err)
		}
	}
}

func (c *domain) handlePublish(msg *event, binding *boundEndpoint) {
	if e := softCumin(binding.expectedTypes, msg.Arguments); e == nil {
		c.app.CallbackSend(binding.callback, msg.Arguments...)
	} else {
		tosend := &errorMessage{Type: pUBLISH, Request: msg.Subscription, Details: make(map[string]interface{}), Arguments: make([]interface{}, 0), Error: e.Error()}

		if err := c.app.Send(tosend); err != nil {
			Warn("error sending message:", err)
		}
	}
}
<|endoftext|>


<commit_before>package form

import (
	"fmt"
	"mime/multipart"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type validateValue struct {
	form         *form
	err          *error
	warning      *string
	name         string
	preferedName string
	fieldsFns    FieldFuncs
	_type        TypeCode
	t            reflect.Type
}

func (f *form) validate(structPtr interface{}) (bool, error) {
	t := reflect.TypeOf(structPtr)
	vc := reflect.ValueOf(structPtr)
	vcc := vc

	switch {
	case isStructPtr(t):
		t = t.Elem()
		vc = vc.Elem()
	default:
		return false, fmt.Errorf("form: '%p' is not a struct pointer", structPtr)
	}

	f.Data[structPtr] = newData()
	data := f.Data[structPtr]

	type _f struct {
		fieldNo      int
		field        reflect.Value
		name         string
		preferedName string
		fieldFns     FieldFuncs
		_type        TypeCode
		t            reflect.Type
		err          error
	}

	fieldM := []*_f{}

	// Populate first
	for fieldNo := 0; fieldNo < t.NumField(); fieldNo++ {
		field := vc.Field(fieldNo)
		if !field.CanSet() {
			continue
		}

		var err error

		name := t.Field(fieldNo).Name
		preferedName := name

		opsFunc := vcc.MethodByName(name + "Field")
		if !opsFunc.IsValid() {
			continue
		}

		val := opsFunc.Call(make([]reflect.Value, 0))
		var fieldFns FieldFuncs
		var ok bool
		if fieldFns, ok = val[0].Interface().(FieldFuncs); !ok {
			continue
		}

		_type := Invalid

		fieldFns.Call("form", map[string]interface{}{
			"type": &_type,
			"name": &preferedName,
		})

		if _type <= Invalid || _type >= terminate {
			continue
		}

		fieldC := &_f{fieldNo, field, name, preferedName, fieldFns, _type, t.Field(fieldNo).Type, nil}

		fieldM = append(fieldM, fieldC)

		if f.Value == nil {
			continue
		}

		switch field.Interface().(type) {

		case string:
			field.Set(reflect.ValueOf(strings.TrimSpace(f.Value.Shift(preferedName))))
		case []string:
			clean := []string{}
			unclean := f.Value.All(preferedName)
			for _, value := range unclean {
				clean = append(clean, strings.TrimSpace(value))
			}
			field.Set(reflect.ValueOf(clean))

		case int64:
			var _v int64
			_v, err = strconv.ParseInt(strings.TrimSpace(f.Value.Shift(preferedName)), 10, 64)
			if err != nil {
				continue
			}
			field.Set(reflect.ValueOf(_v))
		case []int64:
			vs := []int64{}
			vals := f.Value.All(preferedName)
			for _, val := range vals {
				var v int64
				v, err = strconv.ParseInt(strings.TrimSpace(val), 10, 64)
				if err != nil {
					break
				}
				vs = append(vs, v)
			}
			if err != nil {
				continue
			}
			field.Set(reflect.ValueOf(vs))

		case float64:
			var _v float64
			_v, err = strconv.ParseFloat(strings.TrimSpace(f.Value.Shift(preferedName)), 64)
			if err != nil {
				continue
			}
			field.Set(reflect.ValueOf(_v))

		case []float64:
			vs := []float64{}
			vals := f.Value.All(preferedName)
			for _, val := range vals {
				var v float64
				v, err = strconv.ParseFloat(strings.TrimSpace(val), 64)
				if err != nil {
					break
				}
				vs = append(vs, v)
			}
			if err != nil {
				continue
			}
			field.Set(reflect.ValueOf(vs))

		case bool:
			b := false
			if strings.TrimSpace(f.Value.Shift(preferedName)) == "1" {
				b = true
			}
			field.Set(reflect.ValueOf(b))

		case time.Time:
			_v := strings.TrimSpace(f.Value.Shift(preferedName))

			var _time time.Time

			if _v == "" {
				goto blank
			}

			switch _type {
			case InputDatetime:
				_time, err = time.Parse(dateTimeFormat, _v)
			case InputDatetimeLocal:
				_time, err = time.Parse(dateTimeLocalFormat, _v)
			case InputTime:
				_time, err = time.Parse(timeFormat, _v)
			case InputDate:
				_time, err = time.Parse(dateFormat, _v)
			case InputMonth:
				_time, err = time.Parse(monthFormat, _v)
			case InputWeek:
				_vv := strings.Split(_v, "-W")
				if len(_vv) < 2 {
					err = fmt.Errorf(f.T("ErrOutOfBound"))
					continue
				}
				var year int64
				year, err = strconv.ParseInt(_vv[0], 10, 64)
				if err != nil {
					continue
				}
				var week int64
				week, err = strconv.ParseInt(_vv[1], 10, 64)
				if err != nil {
					continue
				}
				_time = StartingDayOfWeek(int(year), int(week))
			}

			if err != nil {
				continue
			}

		blank:

			field.Set(reflect.ValueOf(_time))

		case *multipart.FileHeader:
			fileHeader := f.Value.FileShift(preferedName)
			if fileHeader == nil {
				continue
			}
			field.Set(reflect.ValueOf(fileHeader))
		}

		fieldC.err = err
	}

	jsonUpdate := func(preferedName string, err error, warning string) {
		m := map[string]interface{}{}
		m["valid"] = err == nil
		if m["valid"].(bool) {
			m["error"] = ""
		} else {
			m["error"] = err.Error()
		}
		m["warning"] = warning
		m["name"] = preferedName
		m["count"] = f.vcount
		f.vcount++
		f.JsonData = append(f.JsonData, m)
	}

	hasError := false

	// Now for full on validation.
	for _, item := range fieldM {
		field := item.field
		name := item.name
		preferedName := item.preferedName
		fieldFns := item.fieldFns
		_type := item._type
		t := item.t
		err := item.err

		warning := ""

		va := validateValue{f, &err, &warning, name, preferedName, fieldFns, _type, t}

		if err != nil {
			hasError = true
			jsonUpdate(preferedName, err, warning)
			// No point going further with validation, has the field already failed!
			continue
		}

		switch value := field.Interface().(type) {
		case string:
			va.str(value)
		case []string:
			va.strs(value)
		case int64:
			va.wnum(value)
		case []int64:
			va.wnums(value)
		case float64:
			va.fnum(value)
		case []float64:
			va.fnums(value)
		case bool:
			va.b(value)
		case time.Time:
			va.time(value)
		case *multipart.FileHeader:
			va.file(value)
		}

		if data.Errors[name] == nil {
			va.fieldsFns.Call("ext", map[string]interface{}{
				"error":   &err,
				"warning": &warning,
			})
		}

		if err != nil {
			hasError = true
		}

		data.addError(name, err)
		data.addWarning(name, warning)

		jsonUpdate(preferedName, err, warning)

	}

	return !hasError, nil
}

func (va validateValue) typeError() {
	*(va.err) = fmt.Errorf(va.form.T("ErrType", map[string]interface{}{
		"DataType": va.t.String(),
	}))
}

func (va validateValue) str(value string) {
	switch va._type {
	case InputText, InputPassword, InputSearch, InputHidden, InputUrl, InputTel:
		va.strInputText(value)
	case InputEmail:
		va.strInputEmail(value)
	case InputRadio:
		va.strInputRadio(value)
	case InputColor:
		va.strInputColor(value)
	case Textarea:
		va.strTextarea(value)
	case Select:
		va.strSelect(value)
	default:
		va.typeError()
	}
}

func (va validateValue) strs(values []string) {
	switch va._type {
	case Select:
		va.strsSelect(values)
	default:
		va.typeError()
	}
}

func (va validateValue) wnum(value int64) {
	switch va._type {
	case InputNumber, InputRange, InputHidden:
		va.wnumInputNumber(value)
	case InputRadio:
		va.wnumInputRadio(value)
	case Select:
		va.wnumSelect(value)
	default:
		va.typeError()
	}
}

func (va validateValue) wnums(values []int64) {
	switch va._type {
	case Select:
		va.wnumsSelect(values)
	default:
		va.typeError()
	}
}

func (va validateValue) fnum(value float64) {
	switch va._type {
	case InputNumber, InputRange, InputHidden:
		va.fnumInputNumber(value)
	case InputRadio:
		va.fnumInputRadio(value)
	case Select:
		va.fnumSelect(value)
	default:
		va.typeError()
	}
}

func (va validateValue) fnums(values []float64) {
	switch va._type {
	case Select:
		va.fnumsSelect(values)
	default:
		va.typeError()
	}
}

func (va validateValue) b(value bool) {
	switch va._type {
	case InputCheckbox, InputHidden:
		va.bInputCheckbox(value)
	default:
		va.typeError()
	}
}

func (va validateValue) time(value time.Time) {
	switch va._type {
	case InputDatetime, InputDatetimeLocal, InputTime, InputDate, InputMonth, InputWeek:
		va.timeInputTime(value)
	default:
		va.typeError()
	}
}

func (va validateValue) file(value *multipart.FileHeader) {
	switch va._type {
	case InputFile:
		va.fileInputFile(value)
	default:
		va.typeError()
	}
}
<commit_msg>Minor fix<commit_after>package form

import (
	"fmt"
	"mime/multipart"
	"reflect"
	"strconv"
	"strings"
	"time"
)

type validateValue struct {
	form         *form
	err          *error
	warning      *string
	name         string
	preferedName string
	fieldsFns    FieldFuncs
	_type        TypeCode
	t            reflect.Type
}

func (f *form) validate(structPtr interface{}) (bool, error) {
	t := reflect.TypeOf(structPtr)
	vc := reflect.ValueOf(structPtr)
	vcc := vc

	switch {
	case isStructPtr(t):
		t = t.Elem()
		vc = vc.Elem()
	default:
		return false, fmt.Errorf("form: '%p' is not a struct pointer", structPtr)
	}

	f.Data[structPtr] = newData()
	data := f.Data[structPtr]

	type _f struct {
		fieldNo      int
		field        reflect.Value
		name         string
		preferedName string
		fieldFns     FieldFuncs
		_type        TypeCode
		t            reflect.Type
		err          error
	}

	fieldM := []*_f{}

	// Populate first
	for fieldNo := 0; fieldNo < t.NumField(); fieldNo++ {
		field := vc.Field(fieldNo)
		if !field.CanSet() {
			continue
		}

		var err error

		name := t.Field(fieldNo).Name
		preferedName := name

		opsFunc := vcc.MethodByName(name + "Field")
		if !opsFunc.IsValid() {
			continue
		}

		val := opsFunc.Call(make([]reflect.Value, 0))
		var fieldFns FieldFuncs
		var ok bool
		if fieldFns, ok = val[0].Interface().(FieldFuncs); !ok {
			continue
		}

		_type := Invalid

		fieldFns.Call("form", map[string]interface{}{
			"type": &_type,
			"name": &preferedName,
		})

		if _type <= Invalid || _type >= terminate {
			continue
		}

		fieldC := &_f{fieldNo, field, name, preferedName, fieldFns, _type, t.Field(fieldNo).Type, nil}

		fieldM = append(fieldM, fieldC)

		if f.Value == nil {
			continue
		}

		switch field.Interface().(type) {

		case string:
			field.Set(reflect.ValueOf(strings.TrimSpace(f.Value.Shift(preferedName))))
		case []string:
			clean := []string{}
			unclean := f.Value.All(preferedName)
			for _, value := range unclean {
				clean = append(clean, strings.TrimSpace(value))
			}
			field.Set(reflect.ValueOf(clean))

		case int64:
			var _v int64
			_v, err = strconv.ParseInt(strings.TrimSpace(f.Value.Shift(preferedName)), 10, 64)
			if err != nil {
				continue
			}
			field.Set(reflect.ValueOf(_v))
		case []int64:
			vs := []int64{}
			vals := f.Value.All(preferedName)
			for _, val := range vals {
				var v int64
				v, err = strconv.ParseInt(strings.TrimSpace(val), 10, 64)
				if err != nil {
					break
				}
				vs = append(vs, v)
			}
			if err != nil {
				continue
			}
			field.Set(reflect.ValueOf(vs))

		case float64:
			var _v float64
			_v, err = strconv.ParseFloat(strings.TrimSpace(f.Value.Shift(preferedName)), 64)
			if err != nil {
				continue
			}
			field.Set(reflect.ValueOf(_v))

		case []float64:
			vs := []float64{}
			vals := f.Value.All(preferedName)
			for _, val := range vals {
				var v float64
				v, err = strconv.ParseFloat(strings.TrimSpace(val), 64)
				if err != nil {
					break
				}
				vs = append(vs, v)
			}
			if err != nil {
				continue
			}
			field.Set(reflect.ValueOf(vs))

		case bool:
			b := false
			if strings.TrimSpace(f.Value.Shift(preferedName)) == "1" {
				b = true
			}
			field.Set(reflect.ValueOf(b))

		case time.Time:
			_v := strings.TrimSpace(f.Value.Shift(preferedName))

			var _time time.Time

			if _v == "" {
				goto blank
			}

			switch _type {
			case InputDatetime:
				_time, err = time.Parse(dateTimeFormat, _v)
			case InputDatetimeLocal:
				_time, err = time.Parse(dateTimeLocalFormat, _v)
			case InputTime:
				_time, err = time.Parse(timeFormat, _v)
			case InputDate:
				_time, err = time.Parse(dateFormat, _v)
			case InputMonth:
				_time, err = time.Parse(monthFormat, _v)
			case InputWeek:
				_vv := strings.Split(_v, "-W")
				if len(_vv) < 2 {
					err = fmt.Errorf(f.T("ErrOutOfBound"))
					continue
				}
				var year int64
				year, err = strconv.ParseInt(_vv[0], 10, 64)
				if err != nil {
					continue
				}
				var week int64
				week, err = strconv.ParseInt(_vv[1], 10, 64)
				if err != nil {
					continue
				}
				_time = StartingDayOfWeek(int(year), int(week))
			}

			if err != nil {
				continue
			}

		blank:

			field.Set(reflect.ValueOf(_time))

		case *multipart.FileHeader:
			fileHeader := f.Value.FileShift(preferedName)
			if fileHeader == nil {
				continue
			}
			field.Set(reflect.ValueOf(fileHeader))
		}

		fieldC.err = err
	}

	jsonUpdate := func(preferedName string, err error, warning string) {
		m := map[string]interface{}{}
		m["valid"] = err == nil
		if m["valid"].(bool) {
			m["error"] = ""
		} else {
			m["error"] = err.Error()
		}
		m["warning"] = warning
		m["name"] = preferedName
		m["count"] = f.vcount
		f.vcount++
		f.JsonData = append(f.JsonData, m)
	}

	hasError := false

	// Now for full on validation.
	for _, item := range fieldM {
		field := item.field
		name := item.name
		preferedName := item.preferedName
		fieldFns := item.fieldFns
		_type := item._type
		t := item.t
		err := item.err

		warning := ""

		va := validateValue{f, &err, &warning, name, preferedName, fieldFns, _type, t}

		if err != nil {
			hasError = true
			data.addError(name, err)
			data.addWarning(name, warning)
			jsonUpdate(preferedName, err, warning)
			// No point going further with validation, has the field already failed!
			continue
		}

		switch value := field.Interface().(type) {
		case string:
			va.str(value)
		case []string:
			va.strs(value)
		case int64:
			va.wnum(value)
		case []int64:
			va.wnums(value)
		case float64:
			va.fnum(value)
		case []float64:
			va.fnums(value)
		case bool:
			va.b(value)
		case time.Time:
			va.time(value)
		case *multipart.FileHeader:
			va.file(value)
		}

		if data.Errors[name] == nil {
			va.fieldsFns.Call("ext", map[string]interface{}{
				"error":   &err,
				"warning": &warning,
			})
		}

		if err != nil {
			hasError = true
		}

		data.addError(name, err)
		data.addWarning(name, warning)

		jsonUpdate(preferedName, err, warning)

	}

	return !hasError, nil
}

func (va validateValue) typeError() {
	*(va.err) = fmt.Errorf(va.form.T("ErrType", map[string]interface{}{
		"DataType": va.t.String(),
	}))
}

func (va validateValue) str(value string) {
	switch va._type {
	case InputText, InputPassword, InputSearch, InputHidden, InputUrl, InputTel:
		va.strInputText(value)
	case InputEmail:
		va.strInputEmail(value)
	case InputRadio:
		va.strInputRadio(value)
	case InputColor:
		va.strInputColor(value)
	case Textarea:
		va.strTextarea(value)
	case Select:
		va.strSelect(value)
	default:
		va.typeError()
	}
}

func (va validateValue) strs(values []string) {
	switch va._type {
	case Select:
		va.strsSelect(values)
	default:
		va.typeError()
	}
}

func (va validateValue) wnum(value int64) {
	switch va._type {
	case InputNumber, InputRange, InputHidden:
		va.wnumInputNumber(value)
	case InputRadio:
		va.wnumInputRadio(value)
	case Select:
		va.wnumSelect(value)
	default:
		va.typeError()
	}
}

func (va validateValue) wnums(values []int64) {
	switch va._type {
	case Select:
		va.wnumsSelect(values)
	default:
		va.typeError()
	}
}

func (va validateValue) fnum(value float64) {
	switch va._type {
	case InputNumber, InputRange, InputHidden:
		va.fnumInputNumber(value)
	case InputRadio:
		va.fnumInputRadio(value)
	case Select:
		va.fnumSelect(value)
	default:
		va.typeError()
	}
}

func (va validateValue) fnums(values []float64) {
	switch va._type {
	case Select:
		va.fnumsSelect(values)
	default:
		va.typeError()
	}
}

func (va validateValue) b(value bool) {
	switch va._type {
	case InputCheckbox, InputHidden:
		va.bInputCheckbox(value)
	default:
		va.typeError()
	}
}

func (va validateValue) time(value time.Time) {
	switch va._type {
	case InputDatetime, InputDatetimeLocal, InputTime, InputDate, InputMonth, InputWeek:
		va.timeInputTime(value)
	default:
		va.typeError()
	}
}

func (va validateValue) file(value *multipart.FileHeader) {
	switch va._type {
	case InputFile:
		va.fileInputFile(value)
	default:
		va.typeError()
	}
}
<|endoftext|>


<commit_before>// Copyright 2020 Buf Technologies Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package normalpath provides functions similar to filepath.
//
// A normalized path is a cleaned and to-slash'ed path.
// A validated path validates that a path is relative and does not jump conteext.
package normalpath

import (
	"errors"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

const (
	stringOSPathSeparator    = string(os.PathSeparator)
	relPathJumpContextPrefix = ".." + stringOSPathSeparator
)

var (
	// errNotRelative is the error returned if the path is not relative.
	errNotRelative = errors.New("expected to be relative")
	// errOutsideContextDir is the error returned if the path is outside the context directory.
	errOutsideContextDir = errors.New("is outside the context directory")
)

// Error is a path error.
type Error struct {
	Path string
	Err  error
}

// NewError returns a new Error.
func NewError(path string, err error) *Error {
	return &Error{
		Path: path,
		Err:  err,
	}
}

// Error implements error.
func (e *Error) Error() string {
	errString := ""
	if e.Err != nil {
		errString = e.Err.Error()
	}
	if errString == "" {
		errString = "error"
	}
	return e.Path + ": " + errString
}

// ErrorEquals returns true if err is an Error and err.Error == target.
func ErrorEquals(err error, target error) bool {
	if err == nil {
		return false
	}
	pathError, ok := err.(*Error)
	if !ok {
		return false
	}
	return pathError.Err == target
}

// NormalizeAndValidate normalizes and validates the given path.
//
// This calls Normalize on the path.
// Returns Error if the path is not relative or jumps context.
// This can be used to validate that paths are valid to use with Buckets.
// The error message is safe to pass to users.
func NormalizeAndValidate(path string) (string, error) {
	path = Normalize(path)
	if filepath.IsAbs(path) {
		return "", NewError(path, errNotRelative)
	}
	// https://github.com/bufbuild/buf/issues/51
	if strings.HasPrefix(path, relPathJumpContextPrefix) {
		return "", NewError(path, errOutsideContextDir)
	}
	return path, nil
}

// Normalize normalizes the given path.
//
// This calls filepath.Clean and filepath.ToSlash on the path.
// If the path is "" or ".", this returns ".".
func Normalize(path string) string {
	return filepath.Clean(filepath.ToSlash(path))
}

// Unnormalize unnormalizes the given path.
//
// This calls filepath.FromSlash on the path.
// If the path is "", this returns "".
func Unnormalize(path string) string {
	return filepath.FromSlash(path)
}

// Base is equivalent to filepath.Base.
//
// Normalizes before returning.
func Base(path string) string {
	return Normalize(filepath.Base(Unnormalize(path)))
}

// Dir is equivalent to filepath.Dir.
//
// Normalizes before returning.
func Dir(path string) string {
	return Normalize(filepath.Dir(Unnormalize(path)))
}

// Ext is equivalent to filepath.Ext.
//
// Can return empty string.
func Ext(path string) string {
	return filepath.Ext(Unnormalize(path))
}

// Join is equivalent to filepath.Join.
//
// Empty strings are ignored,
// Can return empty string.
//
// Normalizes before returning otherwise.
func Join(paths ...string) string {
	unnormalized := make([]string, len(paths))
	for i, path := range paths {
		unnormalized[i] = Unnormalize(path)
	}
	value := filepath.Join(unnormalized...)
	if value == "" {
		return ""
	}
	return Normalize(value)
}

// Rel is equivalent to filepath.Rel.
//
// Can return empty string, especially on error.
//
// Normalizes before returning otherwise.
func Rel(basepath string, targpath string) (string, error) {
	path, err := filepath.Rel(Unnormalize(basepath), Unnormalize(targpath))
	if path == "" {
		return "", err
	}
	return Normalize(path), err
}

// ByDir maps the paths into a map from directory via Dir to the original paths.
//
// The paths for each value slice will be sorted.
//
// The path is expected to be normalized.
func ByDir(paths ...string) map[string][]string {
	m := make(map[string][]string)
	for _, path := range paths {
		path = Normalize(path)
		dir := filepath.Dir(path)
		m[dir] = append(m[dir], path)
	}
	for _, dirPaths := range m {
		sort.Strings(dirPaths)
	}
	return m
}

// Components splits the path into it's components.
//
// This calls filepath.Split repeatedly.
//
// The path is expected to be normalized.
func Components(path string) []string {
	var components []string
	dir := Unnormalize(path)
	for {
		var file string
		dir, file = filepath.Split(dir)
		// puts in reverse
		components = append(components, file)
		if dir == stringOSPathSeparator {
			components = append(components, dir)
			break
		}
		dir = strings.TrimSuffix(dir, stringOSPathSeparator)
		if dir == "" {
			break
		}
	}
	// https://github.com/golang/go/wiki/SliceTricks#reversing
	for i := len(components)/2 - 1; i >= 0; i-- {
		opp := len(components) - 1 - i
		components[i], components[opp] = components[opp], components[i]
	}
	for i, component := range components {
		components[i] = Normalize(component)
	}
	return components
}

// MapContainsMatch returns true if the path matches any file or directory in the map.
//
// The path and all keys in m are expected to be normalized and validated.
//
// For a given key x:
//
//   - If x == ".", the path always matches.
//   - If x == path, the path matches.
//   - If x is a directory that contains path, the path matches.
//
// If the map is empty, returns false.
//
// All files and directories in the map are expected to be normalized.
// The path is normalized within this call.
func MapContainsMatch(m map[string]struct{}, path string) bool {
	if len(m) == 0 {
		return false
	}
	if _, ok := m["."]; ok {
		return true
	}
	for curPath := path; curPath != "."; curPath = Dir(curPath) {
		if _, ok := m[curPath]; ok {
			return true
		}
	}
	return false
}

// IsMatch returns true if the value is equal to or contains the path.
//
// The path and value are expected to be normalized and validated.
//
// For a given value:
//
//   - If value == ".", the value contains the path.
//   - If value == path, the value is equal to the path.
//   - If value is a directory that contains path, this returns true.
//
// The path is normalized within this call.
func IsMatch(value string, path string) bool {
	if value == "." {
		return true
	}
	// TODO: can we optimize this with strings.HasPrefix(path, value + "/") somehow?
	for curPath := path; curPath != "."; curPath = Dir(curPath) {
		if value == curPath {
			return true
		}
	}
	return false
}

// MapMatches returns the matching paths in the map.
//
// The path and all keys in m are expected to be normalized and validated.
//
// For a given key x:
//
//   - If x == ".", the path always matches.
//   - If x == path, the path matches.
//   - If x is a directory that contains path, the path matches.
//
// If the map is empty, returns empty map.
//
// All files and directories in the map are expected to be normalized.
// The path is normalized within this call.
func MapMatches(m map[string]struct{}, path string) map[string]struct{} {
	n := make(map[string]struct{})
	if len(m) == 0 {
		return n
	}
	if _, ok := m["."]; ok {
		// also covers if path == ".".
		n["."] = struct{}{}
	}
	for potentialMatch := range m {
		for curPath := path; curPath != "."; curPath = Dir(curPath) {
			if potentialMatch == curPath {
				n[potentialMatch] = struct{}{}
				break
			}
		}
	}
	return n
}

// Transformer transforms and filters paths.
type Transformer interface {
	// Transform transforms and filters the path.
	//
	// If the path is filtered, this will return empty and false.
	// Returns the potentially transformed path.
	//
	// The path is expected to be normalized.
	Transform(path string) (string, bool)
}

// TransformerOption is an option for a new Transformer.
type TransformerOption func(*transformer)

// WithMatcher returns a TransformerOption with the given matcher.
//
// Transform will return false and no path if no matcher returns true.
// If there are no matchers, there is no processing of match rules.
// Multiple matchers are or'ed.
// Applied after strip components.
// Paths are normalized before passing to a matcher.
//
// The default is no matchers.
func WithMatcher(matcher func(name string) bool) TransformerOption {
	return func(transformer *transformer) {
		transformer.matchers = append(transformer.matchers, matcher)
	}
}

// WithExt returns WithMatcher() for Ext(path) == ext.
func WithExt(ext string) TransformerOption {
	return WithMatcher(func(path string) bool { return Ext(path) == ext })
}

// WithExactPath returns WithMatcher() for path == exactPath.
//
// exactPath is normalized within this call.
func WithExactPath(exactPath string) TransformerOption {
	exactPath = Normalize(exactPath)
	return WithMatcher(func(path string) bool { return path == exactPath })
}

// WithStripComponents strips the given number of components from a path.
//
// ApplyOptions will return false and no path if a file does not have this many components.
//
// The default is 0.
func WithStripComponents(stripComponentCount uint32) TransformerOption {
	return func(transformer *transformer) {
		transformer.stripComponentCount = stripComponentCount
	}
}

// NewTransformer returns a new Transformer.
func NewTransformer(options ...TransformerOption) Transformer {
	transformer := &transformer{}
	for _, option := range options {
		option(transformer)
	}
	return transformer
}

type transformer struct {
	matchers            []func(string) bool
	stripComponentCount uint32
}

func (t *transformer) Transform(path string) (string, bool) {
	path, ok := stripComponents(path, int(t.stripComponentCount))
	if !ok {
		return "", false
	}
	if !matches(path, t.matchers...) {
		return "", false
	}
	return path, true
}

// stripComponents strips the specified number of components.
//
// Path expected to be normalized.
// Returns false if the path does not have more than the specified number of components.
func stripComponents(path string, count int) (string, bool) {
	if count == 0 {
		return path, true
	}
	components := Components(path)
	if len(components) <= count {
		return "", false
	}
	return Join(components[count:]...), true
}

// matches returns true if any matcher returns true for the path.
//
// Path expected to be normalized.
// If matchers is empty, returns true.
func matches(path string, matchers ...func(string) bool) bool {
	if len(matchers) == 0 {
		return true
	}
	for _, matcher := range matchers {
		if matcher(path) {
			return true
		}
	}
	return false
}
<commit_msg>Fix normalpath relative path checking<commit_after>// Copyright 2020 Buf Technologies Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package normalpath provides functions similar to filepath.
//
// A normalized path is a cleaned and to-slash'ed path.
// A validated path validates that a path is relative and does not jump conteext.
package normalpath

import (
	"errors"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

const (
	stringOSPathSeparator = string(os.PathSeparator)
	// This has to be with "/" instead of os.PathSeparator as we use this on normalized paths
	normalizedRelPathJumpContextPrefix = "../"
)

var (
	// errNotRelative is the error returned if the path is not relative.
	errNotRelative = errors.New("expected to be relative")
	// errOutsideContextDir is the error returned if the path is outside the context directory.
	errOutsideContextDir = errors.New("is outside the context directory")
)

// Error is a path error.
type Error struct {
	Path string
	Err  error
}

// NewError returns a new Error.
func NewError(path string, err error) *Error {
	return &Error{
		Path: path,
		Err:  err,
	}
}

// Error implements error.
func (e *Error) Error() string {
	errString := ""
	if e.Err != nil {
		errString = e.Err.Error()
	}
	if errString == "" {
		errString = "error"
	}
	return e.Path + ": " + errString
}

// ErrorEquals returns true if err is an Error and err.Error == target.
func ErrorEquals(err error, target error) bool {
	if err == nil {
		return false
	}
	pathError, ok := err.(*Error)
	if !ok {
		return false
	}
	return pathError.Err == target
}

// NormalizeAndValidate normalizes and validates the given path.
//
// This calls Normalize on the path.
// Returns Error if the path is not relative or jumps context.
// This can be used to validate that paths are valid to use with Buckets.
// The error message is safe to pass to users.
func NormalizeAndValidate(path string) (string, error) {
	path = Normalize(path)
	if filepath.IsAbs(path) {
		return "", NewError(path, errNotRelative)
	}
	// https://github.com/bufbuild/buf/issues/51
	if strings.HasPrefix(path, normalizedRelPathJumpContextPrefix) {
		return "", NewError(path, errOutsideContextDir)
	}
	return path, nil
}

// Normalize normalizes the given path.
//
// This calls filepath.Clean and filepath.ToSlash on the path.
// If the path is "" or ".", this returns ".".
func Normalize(path string) string {
	return filepath.Clean(filepath.ToSlash(path))
}

// Unnormalize unnormalizes the given path.
//
// This calls filepath.FromSlash on the path.
// If the path is "", this returns "".
func Unnormalize(path string) string {
	return filepath.FromSlash(path)
}

// Base is equivalent to filepath.Base.
//
// Normalizes before returning.
func Base(path string) string {
	return Normalize(filepath.Base(Unnormalize(path)))
}

// Dir is equivalent to filepath.Dir.
//
// Normalizes before returning.
func Dir(path string) string {
	return Normalize(filepath.Dir(Unnormalize(path)))
}

// Ext is equivalent to filepath.Ext.
//
// Can return empty string.
func Ext(path string) string {
	return filepath.Ext(Unnormalize(path))
}

// Join is equivalent to filepath.Join.
//
// Empty strings are ignored,
// Can return empty string.
//
// Normalizes before returning otherwise.
func Join(paths ...string) string {
	unnormalized := make([]string, len(paths))
	for i, path := range paths {
		unnormalized[i] = Unnormalize(path)
	}
	value := filepath.Join(unnormalized...)
	if value == "" {
		return ""
	}
	return Normalize(value)
}

// Rel is equivalent to filepath.Rel.
//
// Can return empty string, especially on error.
//
// Normalizes before returning otherwise.
func Rel(basepath string, targpath string) (string, error) {
	path, err := filepath.Rel(Unnormalize(basepath), Unnormalize(targpath))
	if path == "" {
		return "", err
	}
	return Normalize(path), err
}

// ByDir maps the paths into a map from directory via Dir to the original paths.
//
// The paths for each value slice will be sorted.
//
// The path is expected to be normalized.
func ByDir(paths ...string) map[string][]string {
	m := make(map[string][]string)
	for _, path := range paths {
		path = Normalize(path)
		dir := filepath.Dir(path)
		m[dir] = append(m[dir], path)
	}
	for _, dirPaths := range m {
		sort.Strings(dirPaths)
	}
	return m
}

// Components splits the path into it's components.
//
// This calls filepath.Split repeatedly.
//
// The path is expected to be normalized.
func Components(path string) []string {
	var components []string
	dir := Unnormalize(path)
	for {
		var file string
		dir, file = filepath.Split(dir)
		// puts in reverse
		components = append(components, file)
		if dir == stringOSPathSeparator {
			components = append(components, dir)
			break
		}
		dir = strings.TrimSuffix(dir, stringOSPathSeparator)
		if dir == "" {
			break
		}
	}
	// https://github.com/golang/go/wiki/SliceTricks#reversing
	for i := len(components)/2 - 1; i >= 0; i-- {
		opp := len(components) - 1 - i
		components[i], components[opp] = components[opp], components[i]
	}
	for i, component := range components {
		components[i] = Normalize(component)
	}
	return components
}

// MapContainsMatch returns true if the path matches any file or directory in the map.
//
// The path and all keys in m are expected to be normalized and validated.
//
// For a given key x:
//
//   - If x == ".", the path always matches.
//   - If x == path, the path matches.
//   - If x is a directory that contains path, the path matches.
//
// If the map is empty, returns false.
//
// All files and directories in the map are expected to be normalized.
// The path is normalized within this call.
func MapContainsMatch(m map[string]struct{}, path string) bool {
	if len(m) == 0 {
		return false
	}
	if _, ok := m["."]; ok {
		return true
	}
	for curPath := path; curPath != "."; curPath = Dir(curPath) {
		if _, ok := m[curPath]; ok {
			return true
		}
	}
	return false
}

// IsMatch returns true if the value is equal to or contains the path.
//
// The path and value are expected to be normalized and validated.
//
// For a given value:
//
//   - If value == ".", the value contains the path.
//   - If value == path, the value is equal to the path.
//   - If value is a directory that contains path, this returns true.
//
// The path is normalized within this call.
func IsMatch(value string, path string) bool {
	if value == "." {
		return true
	}
	// TODO: can we optimize this with strings.HasPrefix(path, value + "/") somehow?
	for curPath := path; curPath != "."; curPath = Dir(curPath) {
		if value == curPath {
			return true
		}
	}
	return false
}

// MapMatches returns the matching paths in the map.
//
// The path and all keys in m are expected to be normalized and validated.
//
// For a given key x:
//
//   - If x == ".", the path always matches.
//   - If x == path, the path matches.
//   - If x is a directory that contains path, the path matches.
//
// If the map is empty, returns empty map.
//
// All files and directories in the map are expected to be normalized.
// The path is normalized within this call.
func MapMatches(m map[string]struct{}, path string) map[string]struct{} {
	n := make(map[string]struct{})
	if len(m) == 0 {
		return n
	}
	if _, ok := m["."]; ok {
		// also covers if path == ".".
		n["."] = struct{}{}
	}
	for potentialMatch := range m {
		for curPath := path; curPath != "."; curPath = Dir(curPath) {
			if potentialMatch == curPath {
				n[potentialMatch] = struct{}{}
				break
			}
		}
	}
	return n
}

// Transformer transforms and filters paths.
type Transformer interface {
	// Transform transforms and filters the path.
	//
	// If the path is filtered, this will return empty and false.
	// Returns the potentially transformed path.
	//
	// The path is expected to be normalized.
	Transform(path string) (string, bool)
}

// TransformerOption is an option for a new Transformer.
type TransformerOption func(*transformer)

// WithMatcher returns a TransformerOption with the given matcher.
//
// Transform will return false and no path if no matcher returns true.
// If there are no matchers, there is no processing of match rules.
// Multiple matchers are or'ed.
// Applied after strip components.
// Paths are normalized before passing to a matcher.
//
// The default is no matchers.
func WithMatcher(matcher func(name string) bool) TransformerOption {
	return func(transformer *transformer) {
		transformer.matchers = append(transformer.matchers, matcher)
	}
}

// WithExt returns WithMatcher() for Ext(path) == ext.
func WithExt(ext string) TransformerOption {
	return WithMatcher(func(path string) bool { return Ext(path) == ext })
}

// WithExactPath returns WithMatcher() for path == exactPath.
//
// exactPath is normalized within this call.
func WithExactPath(exactPath string) TransformerOption {
	exactPath = Normalize(exactPath)
	return WithMatcher(func(path string) bool { return path == exactPath })
}

// WithStripComponents strips the given number of components from a path.
//
// ApplyOptions will return false and no path if a file does not have this many components.
//
// The default is 0.
func WithStripComponents(stripComponentCount uint32) TransformerOption {
	return func(transformer *transformer) {
		transformer.stripComponentCount = stripComponentCount
	}
}

// NewTransformer returns a new Transformer.
func NewTransformer(options ...TransformerOption) Transformer {
	transformer := &transformer{}
	for _, option := range options {
		option(transformer)
	}
	return transformer
}

type transformer struct {
	matchers            []func(string) bool
	stripComponentCount uint32
}

func (t *transformer) Transform(path string) (string, bool) {
	path, ok := stripComponents(path, int(t.stripComponentCount))
	if !ok {
		return "", false
	}
	if !matches(path, t.matchers...) {
		return "", false
	}
	return path, true
}

// stripComponents strips the specified number of components.
//
// Path expected to be normalized.
// Returns false if the path does not have more than the specified number of components.
func stripComponents(path string, count int) (string, bool) {
	if count == 0 {
		return path, true
	}
	components := Components(path)
	if len(components) <= count {
		return "", false
	}
	return Join(components[count:]...), true
}

// matches returns true if any matcher returns true for the path.
//
// Path expected to be normalized.
// If matchers is empty, returns true.
func matches(path string, matchers ...func(string) bool) bool {
	if len(matchers) == 0 {
		return true
	}
	for _, matcher := range matchers {
		if matcher(path) {
			return true
		}
	}
	return false
}
<|endoftext|>