<commit_before>package gval

import (
	"context"
	"fmt"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

// Selector allows for custom variable selection from structs
//
// Return value is again handled with variable() until end of the given path
type Selector interface {
	SelectGVal(c context.Context, key string) (interface{}, error)
}

// Evaluable evaluates given parameter
type Evaluable func(c context.Context, parameter interface{}) (interface{}, error)

//EvalInt evaluates given parameter to an int
func (e Evaluable) EvalInt(c context.Context, parameter interface{}) (int, error) {
	v, err := e(c, parameter)
	if err != nil {
		return 0, err
	}

	f, ok := convertToFloat(v)
	if !ok {
		return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
	}
	return int(f), nil
}

//EvalFloat64 evaluates given parameter to a float64
func (e Evaluable) EvalFloat64(c context.Context, parameter interface{}) (float64, error) {
	v, err := e(c, parameter)
	if err != nil {
		return 0, err
	}

	f, ok := convertToFloat(v)
	if !ok {
		return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
	}
	return f, nil
}

//EvalBool evaluates given parameter to a bool
func (e Evaluable) EvalBool(c context.Context, parameter interface{}) (bool, error) {
	v, err := e(c, parameter)
	if err != nil {
		return false, err
	}

	b, ok := convertToBool(v)
	if !ok {
		return false, fmt.Errorf("expected bool but got %v (%T)", v, v)
	}
	return b, nil
}

//EvalString evaluates given parameter to a string
func (e Evaluable) EvalString(c context.Context, parameter interface{}) (string, error) {
	o, err := e(c, parameter)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%v", o), nil
}

//Const Evaluable represents given constant
func (*Parser) Const(value interface{}) Evaluable {
	return constant(value)
}

func constant(value interface{}) Evaluable {
	return func(c context.Context, v interface{}) (interface{}, error) {
		return value, nil
	}
}

//Var Evaluable represents value at given path.
//It supports with default language VariableSelector:
//	map[interface{}]interface{},
//	map[string]interface{} and
// 	[]interface{} and via reflect
//	struct fields,
//	struct methods,
//	slices and
//  map with int or string key.
func (p *Parser) Var(path ...Evaluable) Evaluable {
	if p.Language.selector == nil {
		return variable(path)
	}
	return p.Language.selector(path)
}

// Evaluables is a slice of Evaluable.
type Evaluables []Evaluable

// EvalStrings evaluates given parameter to a string slice
func (evs Evaluables) EvalStrings(c context.Context, parameter interface{}) ([]string, error) {
	strs := make([]string, len(evs))
	for i, p := range evs {
		k, err := p.EvalString(c, parameter)
		if err != nil {
			return nil, err
		}
		strs[i] = k
	}
	return strs, nil
}

func variable(path Evaluables) Evaluable {
	return func(c context.Context, v interface{}) (interface{}, error) {
		keys, err := path.EvalStrings(c, v)
		if err != nil {
			return nil, err
		}
		for i, k := range keys {
			switch o := v.(type) {
			case Selector:
				v, err = o.SelectGVal(c, k)
				if err != nil {
					return nil, fmt.Errorf("failed to select '%s' on %T: %w", k, o, err)
				}
			case map[interface{}]interface{}:
				v = o[k]
				continue
			case map[string]interface{}:
				v = o[k]
				continue
			case []interface{}:
				if i, err := strconv.Atoi(k); err == nil && i >= 0 && len(o) > i {
					v = o[i]
					continue
				}
			default:
				var ok bool
				v, ok = reflectSelect(k, o)
				if !ok {
					return nil, fmt.Errorf("unknown parameter %s", strings.Join(keys[:i+1], "."))
				}
			}
		}
		return v, nil
	}
}

func reflectSelect(key string, value interface{}) (selection interface{}, ok bool) {
	vv := reflect.ValueOf(value)
	vvElem := resolvePotentialPointer(vv)

	switch vvElem.Kind() {
	case reflect.Map:
		mapKey, ok := reflectConvertTo(vv.Type().Key().Kind(), key)
		if !ok {
			return nil, false
		}

		vvElem = vv.MapIndex(reflect.ValueOf(mapKey))
		vvElem = resolvePotentialPointer(vvElem)

		if vvElem.IsValid() {
			return vvElem.Interface(), true
		}
	case reflect.Slice:
		if i, err := strconv.Atoi(key); err == nil && i >= 0 && vv.Len() > i {
			vvElem = resolvePotentialPointer(vv.Index(i))
			return vvElem.Interface(), true
		}
	case reflect.Struct:
		field := vvElem.FieldByName(key)
		if field.IsValid() {
			return field.Interface(), true
		}

		method := vv.MethodByName(key)
		if method.IsValid() {
			return method.Interface(), true
		}
	}
	return nil, false
}

func resolvePotentialPointer(value reflect.Value) reflect.Value {
	if value.Kind() == reflect.Ptr {
		return value.Elem()
	}
	return value
}

func reflectConvertTo(k reflect.Kind, value string) (interface{}, bool) {
	switch k {
	case reflect.String:
		return value, true
	case reflect.Int:
		if i, err := strconv.Atoi(value); err == nil {
			return i, true
		}
	}
	return nil, false
}

func (*Parser) callFunc(fun function, args ...Evaluable) Evaluable {
	return func(c context.Context, v interface{}) (ret interface{}, err error) {
		a := make([]interface{}, len(args))
		for i, arg := range args {
			ai, err := arg(c, v)
			if err != nil {
				return nil, err
			}
			a[i] = ai
		}
		return fun(c, a...)
	}
}

func (*Parser) callEvaluable(fullname string, fun Evaluable, args ...Evaluable) Evaluable {
	return func(c context.Context, v interface{}) (ret interface{}, err error) {
		f, err := fun(c, v)

		if err != nil {
			return nil, fmt.Errorf("could not call function: %v", err)
		}

		defer func() {
			if r := recover(); r != nil {
				err = fmt.Errorf("failed to execute function '%s': %s", fullname, r)
				ret = nil
			}
		}()

		ff := reflect.ValueOf(f)

		if ff.Kind() != reflect.Func {
			return nil, fmt.Errorf("could not call '%s' type %T", fullname, f)
		}

		a := make([]reflect.Value, len(args))
		for i := range args {
			arg, err := args[i](c, v)
			if err != nil {
				return nil, err
			}
			a[i] = reflect.ValueOf(arg)
		}

		rr := ff.Call(a)

		r := make([]interface{}, len(rr))
		for i, e := range rr {
			r[i] = e.Interface()
		}

		errorInterface := reflect.TypeOf((*error)(nil)).Elem()
		if len(r) > 0 && ff.Type().Out(len(r)-1).Implements(errorInterface) {
			if r[len(r)-1] != nil {
				err = r[len(r)-1].(error)
			}
			r = r[0 : len(r)-1]
		}

		switch len(r) {
		case 0:
			return err, nil
		case 1:
			return r[0], err
		default:
			return r, err
		}
	}
}

//IsConst returns if the Evaluable is a Parser.Const() value
func (e Evaluable) IsConst() bool {
	pc := reflect.ValueOf(constant(nil)).Pointer()
	pe := reflect.ValueOf(e).Pointer()
	return pc == pe
}

func regEx(a, b Evaluable) (Evaluable, error) {
	if !b.IsConst() {
		return func(c context.Context, o interface{}) (interface{}, error) {
			a, err := a.EvalString(c, o)
			if err != nil {
				return nil, err
			}
			b, err := b.EvalString(c, o)
			if err != nil {
				return nil, err
			}
			matched, err := regexp.MatchString(b, a)
			return matched, err
		}, nil
	}
	s, err := b.EvalString(nil, nil)
	if err != nil {
		return nil, err
	}
	regex, err := regexp.Compile(s)
	if err != nil {
		return nil, err
	}
	return func(c context.Context, v interface{}) (interface{}, error) {
		s, err := a.EvalString(c, v)
		if err != nil {
			return nil, err
		}
		return regex.MatchString(s), nil
	}, nil
}

func notRegEx(a, b Evaluable) (Evaluable, error) {
	if !b.IsConst() {
		return func(c context.Context, o interface{}) (interface{}, error) {
			a, err := a.EvalString(c, o)
			if err != nil {
				return nil, err
			}
			b, err := b.EvalString(c, o)
			if err != nil {
				return nil, err
			}
			matched, err := regexp.MatchString(b, a)
			return !matched, err
		}, nil
	}
	s, err := b.EvalString(nil, nil)
	if err != nil {
		return nil, err
	}
	regex, err := regexp.Compile(s)
	if err != nil {
		return nil, err
	}
	return func(c context.Context, v interface{}) (interface{}, error) {
		s, err := a.EvalString(c, v)
		if err != nil {
			return nil, err
		}
		return !regex.MatchString(s), nil
	}, nil
}
<commit_msg>Add missing continue in for Selector case<commit_after>package gval

import (
	"context"
	"fmt"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

// Selector allows for custom variable selection from structs
//
// Return value is again handled with variable() until end of the given path
type Selector interface {
	SelectGVal(c context.Context, key string) (interface{}, error)
}

// Evaluable evaluates given parameter
type Evaluable func(c context.Context, parameter interface{}) (interface{}, error)

//EvalInt evaluates given parameter to an int
func (e Evaluable) EvalInt(c context.Context, parameter interface{}) (int, error) {
	v, err := e(c, parameter)
	if err != nil {
		return 0, err
	}

	f, ok := convertToFloat(v)
	if !ok {
		return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
	}
	return int(f), nil
}

//EvalFloat64 evaluates given parameter to a float64
func (e Evaluable) EvalFloat64(c context.Context, parameter interface{}) (float64, error) {
	v, err := e(c, parameter)
	if err != nil {
		return 0, err
	}

	f, ok := convertToFloat(v)
	if !ok {
		return 0, fmt.Errorf("expected number but got %v (%T)", v, v)
	}
	return f, nil
}

//EvalBool evaluates given parameter to a bool
func (e Evaluable) EvalBool(c context.Context, parameter interface{}) (bool, error) {
	v, err := e(c, parameter)
	if err != nil {
		return false, err
	}

	b, ok := convertToBool(v)
	if !ok {
		return false, fmt.Errorf("expected bool but got %v (%T)", v, v)
	}
	return b, nil
}

//EvalString evaluates given parameter to a string
func (e Evaluable) EvalString(c context.Context, parameter interface{}) (string, error) {
	o, err := e(c, parameter)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%v", o), nil
}

//Const Evaluable represents given constant
func (*Parser) Const(value interface{}) Evaluable {
	return constant(value)
}

func constant(value interface{}) Evaluable {
	return func(c context.Context, v interface{}) (interface{}, error) {
		return value, nil
	}
}

//Var Evaluable represents value at given path.
//It supports with default language VariableSelector:
//	map[interface{}]interface{},
//	map[string]interface{} and
// 	[]interface{} and via reflect
//	struct fields,
//	struct methods,
//	slices and
//  map with int or string key.
func (p *Parser) Var(path ...Evaluable) Evaluable {
	if p.Language.selector == nil {
		return variable(path)
	}
	return p.Language.selector(path)
}

// Evaluables is a slice of Evaluable.
type Evaluables []Evaluable

// EvalStrings evaluates given parameter to a string slice
func (evs Evaluables) EvalStrings(c context.Context, parameter interface{}) ([]string, error) {
	strs := make([]string, len(evs))
	for i, p := range evs {
		k, err := p.EvalString(c, parameter)
		if err != nil {
			return nil, err
		}
		strs[i] = k
	}
	return strs, nil
}

func variable(path Evaluables) Evaluable {
	return func(c context.Context, v interface{}) (interface{}, error) {
		keys, err := path.EvalStrings(c, v)
		if err != nil {
			return nil, err
		}
		for i, k := range keys {
			switch o := v.(type) {
			case Selector:
				v, err = o.SelectGVal(c, k)
				if err != nil {
					return nil, fmt.Errorf("failed to select '%s' on %T: %w", k, o, err)
				}
				continue
			case map[interface{}]interface{}:
				v = o[k]
				continue
			case map[string]interface{}:
				v = o[k]
				continue
			case []interface{}:
				if i, err := strconv.Atoi(k); err == nil && i >= 0 && len(o) > i {
					v = o[i]
					continue
				}
			default:
				var ok bool
				v, ok = reflectSelect(k, o)
				if !ok {
					return nil, fmt.Errorf("unknown parameter %s", strings.Join(keys[:i+1], "."))
				}
			}
		}
		return v, nil
	}
}

func reflectSelect(key string, value interface{}) (selection interface{}, ok bool) {
	vv := reflect.ValueOf(value)
	vvElem := resolvePotentialPointer(vv)

	switch vvElem.Kind() {
	case reflect.Map:
		mapKey, ok := reflectConvertTo(vv.Type().Key().Kind(), key)
		if !ok {
			return nil, false
		}

		vvElem = vv.MapIndex(reflect.ValueOf(mapKey))
		vvElem = resolvePotentialPointer(vvElem)

		if vvElem.IsValid() {
			return vvElem.Interface(), true
		}
	case reflect.Slice:
		if i, err := strconv.Atoi(key); err == nil && i >= 0 && vv.Len() > i {
			vvElem = resolvePotentialPointer(vv.Index(i))
			return vvElem.Interface(), true
		}
	case reflect.Struct:
		field := vvElem.FieldByName(key)
		if field.IsValid() {
			return field.Interface(), true
		}

		method := vv.MethodByName(key)
		if method.IsValid() {
			return method.Interface(), true
		}
	}
	return nil, false
}

func resolvePotentialPointer(value reflect.Value) reflect.Value {
	if value.Kind() == reflect.Ptr {
		return value.Elem()
	}
	return value
}

func reflectConvertTo(k reflect.Kind, value string) (interface{}, bool) {
	switch k {
	case reflect.String:
		return value, true
	case reflect.Int:
		if i, err := strconv.Atoi(value); err == nil {
			return i, true
		}
	}
	return nil, false
}

func (*Parser) callFunc(fun function, args ...Evaluable) Evaluable {
	return func(c context.Context, v interface{}) (ret interface{}, err error) {
		a := make([]interface{}, len(args))
		for i, arg := range args {
			ai, err := arg(c, v)
			if err != nil {
				return nil, err
			}
			a[i] = ai
		}
		return fun(c, a...)
	}
}

func (*Parser) callEvaluable(fullname string, fun Evaluable, args ...Evaluable) Evaluable {
	return func(c context.Context, v interface{}) (ret interface{}, err error) {
		f, err := fun(c, v)

		if err != nil {
			return nil, fmt.Errorf("could not call function: %v", err)
		}

		defer func() {
			if r := recover(); r != nil {
				err = fmt.Errorf("failed to execute function '%s': %s", fullname, r)
				ret = nil
			}
		}()

		ff := reflect.ValueOf(f)

		if ff.Kind() != reflect.Func {
			return nil, fmt.Errorf("could not call '%s' type %T", fullname, f)
		}

		a := make([]reflect.Value, len(args))
		for i := range args {
			arg, err := args[i](c, v)
			if err != nil {
				return nil, err
			}
			a[i] = reflect.ValueOf(arg)
		}

		rr := ff.Call(a)

		r := make([]interface{}, len(rr))
		for i, e := range rr {
			r[i] = e.Interface()
		}

		errorInterface := reflect.TypeOf((*error)(nil)).Elem()
		if len(r) > 0 && ff.Type().Out(len(r)-1).Implements(errorInterface) {
			if r[len(r)-1] != nil {
				err = r[len(r)-1].(error)
			}
			r = r[0 : len(r)-1]
		}

		switch len(r) {
		case 0:
			return err, nil
		case 1:
			return r[0], err
		default:
			return r, err
		}
	}
}

//IsConst returns if the Evaluable is a Parser.Const() value
func (e Evaluable) IsConst() bool {
	pc := reflect.ValueOf(constant(nil)).Pointer()
	pe := reflect.ValueOf(e).Pointer()
	return pc == pe
}

func regEx(a, b Evaluable) (Evaluable, error) {
	if !b.IsConst() {
		return func(c context.Context, o interface{}) (interface{}, error) {
			a, err := a.EvalString(c, o)
			if err != nil {
				return nil, err
			}
			b, err := b.EvalString(c, o)
			if err != nil {
				return nil, err
			}
			matched, err := regexp.MatchString(b, a)
			return matched, err
		}, nil
	}
	s, err := b.EvalString(nil, nil)
	if err != nil {
		return nil, err
	}
	regex, err := regexp.Compile(s)
	if err != nil {
		return nil, err
	}
	return func(c context.Context, v interface{}) (interface{}, error) {
		s, err := a.EvalString(c, v)
		if err != nil {
			return nil, err
		}
		return regex.MatchString(s), nil
	}, nil
}

func notRegEx(a, b Evaluable) (Evaluable, error) {
	if !b.IsConst() {
		return func(c context.Context, o interface{}) (interface{}, error) {
			a, err := a.EvalString(c, o)
			if err != nil {
				return nil, err
			}
			b, err := b.EvalString(c, o)
			if err != nil {
				return nil, err
			}
			matched, err := regexp.MatchString(b, a)
			return !matched, err
		}, nil
	}
	s, err := b.EvalString(nil, nil)
	if err != nil {
		return nil, err
	}
	regex, err := regexp.Compile(s)
	if err != nil {
		return nil, err
	}
	return func(c context.Context, v interface{}) (interface{}, error) {
		s, err := a.EvalString(c, v)
		if err != nil {
			return nil, err
		}
		return !regex.MatchString(s), nil
	}, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, 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 etcdmain

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"runtime"
	"strconv"
	"time"

	"github.com/coreos/etcd/discovery"
	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/pkg/cors"
	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/osutil"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/proxy"
	"github.com/coreos/etcd/rafthttp"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
)

type dirType string

var plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdmain")

const (
	// the owner can make/remove files inside the directory
	privateDirMode = 0700
)

var (
	dirMember = dirType("member")
	dirProxy  = dirType("proxy")
	dirEmpty  = dirType("empty")
)

func Main() {
	capnslog.SetFormatter(capnslog.NewStringFormatter(os.Stderr))
	cfg := NewConfig()
	err := cfg.Parse(os.Args[1:])
	if err != nil {
		plog.Errorf("error verifying flags, %v. See 'etcd --help'.", err)
		switch err {
		case errUnsetAdvertiseClientURLsFlag:
			plog.Errorf("When listening on specific address(es), this etcd process must advertise accessible url(s) to each connected client.")
		}
		os.Exit(1)
	}
	setupLogging(cfg)

	var stopped <-chan struct{}

	GoMaxProcs := 1
	if envMaxProcs, err := strconv.Atoi(os.Getenv("GOMAXPROCS")); err == nil {
		GoMaxProcs = envMaxProcs
	}
	plog.Infof("setting maximum number of CPUs to %d, total number of available CPUs is %d", GoMaxProcs, runtime.NumCPU())
	runtime.GOMAXPROCS(GoMaxProcs)

	// TODO: check whether fields are set instead of whether fields have default value
	if cfg.name != defaultName && cfg.initialCluster == initialClusterFromName(defaultName) {
		cfg.initialCluster = initialClusterFromName(cfg.name)
	}

	if cfg.dir == "" {
		cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
		plog.Warningf("no data-dir provided, using default data-dir ./%s", cfg.dir)
	}

	which := identifyDataDirOrDie(cfg.dir)
	if which != dirEmpty {
		plog.Noticef("the server is already initialized as %v before, starting as etcd %v...", which, which)
	}

	shouldProxy := cfg.isProxy() || which == dirProxy
	if !shouldProxy {
		stopped, err = startEtcd(cfg)
		if err == discovery.ErrFullCluster && cfg.shouldFallbackToProxy() {
			plog.Noticef("discovery cluster full, falling back to %s", fallbackFlagProxy)
			shouldProxy = true
		}
	}
	if shouldProxy {
		err = startProxy(cfg)
	}
	if err != nil {
		switch err {
		case discovery.ErrDuplicateID:
			plog.Errorf("member %q has previously registered with discovery service token (%s).", cfg.name, cfg.durl)
			plog.Errorf("But etcd could not find vaild cluster configuration in the given data dir (%s).", cfg.dir)
			plog.Infof("Please check the given data dir path if the previous bootstrap succeeded")
			plog.Infof("or use a new discovery token if the previous bootstrap failed.")
		default:
			plog.Fatalf("%v", err)
		}
	}

	osutil.HandleInterrupts()

	<-stopped
	osutil.Exit(0)
}

// startEtcd launches the etcd server and HTTP handlers for client/server communication.
func startEtcd(cfg *config) (<-chan struct{}, error) {
	urlsmap, token, err := getPeerURLsMapAndToken(cfg)
	if err != nil {
		return nil, fmt.Errorf("error setting up initial cluster: %v", err)
	}

	pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, rafthttp.DialTimeout, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
	if err != nil {
		return nil, err
	}

	if !cfg.peerTLSInfo.Empty() {
		plog.Infof("peerTLS: %s", cfg.peerTLSInfo)
	}
	plns := make([]net.Listener, 0)
	for _, u := range cfg.lpurls {
		var l net.Listener
		l, err = transport.NewTimeoutListener(u.Host, u.Scheme, cfg.peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
		if err != nil {
			return nil, err
		}

		urlStr := u.String()
		plog.Info("listening for peers on ", urlStr)
		defer func() {
			if err != nil {
				l.Close()
				plog.Info("stopping listening for peers on ", urlStr)
			}
		}()
		plns = append(plns, l)
	}

	if !cfg.clientTLSInfo.Empty() {
		plog.Infof("clientTLS: %s", cfg.clientTLSInfo)
	}
	clns := make([]net.Listener, 0)
	for _, u := range cfg.lcurls {
		var l net.Listener
		l, err = transport.NewKeepAliveListener(u.Host, u.Scheme, cfg.clientTLSInfo)
		if err != nil {
			return nil, err
		}

		urlStr := u.String()
		plog.Info("listening for client requests on ", urlStr)
		defer func() {
			if err != nil {
				l.Close()
				plog.Info("stopping listening for client requests on ", urlStr)
			}
		}()
		clns = append(clns, l)
	}

	srvcfg := &etcdserver.ServerConfig{
		Name:                cfg.name,
		ClientURLs:          cfg.acurls,
		PeerURLs:            cfg.apurls,
		DataDir:             cfg.dir,
		SnapCount:           cfg.snapCount,
		MaxSnapFiles:        cfg.maxSnapFiles,
		MaxWALFiles:         cfg.maxWalFiles,
		InitialPeerURLsMap:  urlsmap,
		InitialClusterToken: token,
		DiscoveryURL:        cfg.durl,
		DiscoveryProxy:      cfg.dproxy,
		NewCluster:          cfg.isNewCluster(),
		ForceNewCluster:     cfg.forceNewCluster,
		Transport:           pt,
		TickMs:              cfg.TickMs,
		ElectionTicks:       cfg.electionTicks(),
	}
	var s *etcdserver.EtcdServer
	s, err = etcdserver.NewServer(srvcfg)
	if err != nil {
		return nil, err
	}
	s.Start()
	osutil.RegisterInterruptHandler(s.Stop)

	if cfg.corsInfo.String() != "" {
		plog.Infof("cors = %s", cfg.corsInfo)
	}
	ch := &cors.CORSHandler{
		Handler: etcdhttp.NewClientHandler(s),
		Info:    cfg.corsInfo,
	}
	ph := etcdhttp.NewPeerHandler(s.Cluster(), s.RaftHandler())
	// Start the peer server in a goroutine
	for _, l := range plns {
		go func(l net.Listener) {
			plog.Fatal(serveHTTP(l, ph, 5*time.Minute))
		}(l)
	}
	// Start a client server goroutine for each listen address
	for _, l := range clns {
		go func(l net.Listener) {
			// read timeout does not work with http close notify
			// TODO: https://github.com/golang/go/issues/9524
			plog.Fatal(serveHTTP(l, ch, 0))
		}(l)
	}
	return s.StopNotify(), nil
}

// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
func startProxy(cfg *config) error {
	urlsmap, _, err := getPeerURLsMapAndToken(cfg)
	if err != nil {
		return fmt.Errorf("error setting up initial cluster: %v", err)
	}

	if cfg.durl != "" {
		s, err := discovery.GetCluster(cfg.durl, cfg.dproxy)
		if err != nil {
			return err
		}
		if urlsmap, err = types.NewURLsMap(s); err != nil {
			return err
		}
	}

	pt, err := transport.NewTransport(cfg.clientTLSInfo)
	pt.MaxIdleConnsPerHost = proxy.DefaultMaxIdleConnsPerHost
	if err != nil {
		return err
	}

	tr, err := transport.NewTransport(cfg.peerTLSInfo)
	if err != nil {
		return err
	}

	cfg.dir = path.Join(cfg.dir, "proxy")
	err = os.MkdirAll(cfg.dir, 0700)
	if err != nil {
		return err
	}

	var peerURLs []string
	clusterfile := path.Join(cfg.dir, "cluster")

	b, err := ioutil.ReadFile(clusterfile)
	switch {
	case err == nil:
		urls := struct{ PeerURLs []string }{}
		err := json.Unmarshal(b, &urls)
		if err != nil {
			return err
		}
		peerURLs = urls.PeerURLs
		plog.Infof("proxy: using peer urls %v from cluster file ./%s", peerURLs, clusterfile)
	case os.IsNotExist(err):
		peerURLs = urlsmap.URLs()
		plog.Infof("proxy: using peer urls %v ", peerURLs)
	default:
		return err
	}

	clientURLs := []string{}
	uf := func() []string {
		gcls, err := etcdserver.GetClusterFromRemotePeers(peerURLs, tr)
		// TODO: remove the 2nd check when we fix GetClusterFromPeers
		// GetClusterFromPeers should not return nil error with an invaild empty cluster
		if err != nil {
			plog.Warningf("proxy: %v", err)
			return []string{}
		}
		if len(gcls.Members()) == 0 {
			return clientURLs
		}
		clientURLs = gcls.ClientURLs()

		urls := struct{ PeerURLs []string }{gcls.PeerURLs()}
		b, err := json.Marshal(urls)
		if err != nil {
			plog.Warningf("proxy: error on marshal peer urls %s", err)
			return clientURLs
		}

		err = ioutil.WriteFile(clusterfile+".bak", b, 0600)
		if err != nil {
			plog.Warningf("proxy: error on writing urls %s", err)
			return clientURLs
		}
		err = os.Rename(clusterfile+".bak", clusterfile)
		if err != nil {
			plog.Warningf("proxy: error on updating clusterfile %s", err)
			return clientURLs
		}
		if !reflect.DeepEqual(gcls.PeerURLs(), peerURLs) {
			plog.Noticef("proxy: updated peer urls in cluster file from %v to %v", peerURLs, gcls.PeerURLs())
		}
		peerURLs = gcls.PeerURLs()

		return clientURLs
	}
	ph := proxy.NewHandler(pt, uf)
	ph = &cors.CORSHandler{
		Handler: ph,
		Info:    cfg.corsInfo,
	}

	if cfg.isReadonlyProxy() {
		ph = proxy.NewReadonlyHandler(ph)
	}
	// Start a proxy server goroutine for each listen address
	for _, u := range cfg.lcurls {
		l, err := transport.NewListener(u.Host, u.Scheme, cfg.clientTLSInfo)
		if err != nil {
			return err
		}

		host := u.Host
		go func() {
			plog.Info("proxy: listening for client requests on ", host)
			plog.Fatal(http.Serve(l, ph))
		}()
	}
	return nil
}

// getPeerURLsMapAndToken sets up an initial peer URLsMap and cluster token for bootstrap or discovery.
func getPeerURLsMapAndToken(cfg *config) (urlsmap types.URLsMap, token string, err error) {
	switch {
	case cfg.durl != "":
		urlsmap = types.URLsMap{}
		// If using discovery, generate a temporary cluster based on
		// self's advertised peer URLs
		urlsmap[cfg.name] = cfg.apurls
		token = cfg.durl
	case cfg.dnsCluster != "":
		var clusterStr string
		clusterStr, token, err = discovery.SRVGetCluster(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls)
		if err != nil {
			return nil, "", err
		}
		urlsmap, err = types.NewURLsMap(clusterStr)
	default:
		// We're statically configured, and cluster has appropriately been set.
		urlsmap, err = types.NewURLsMap(cfg.initialCluster)
		token = cfg.initialClusterToken
	}
	return urlsmap, token, err
}

// identifyDataDirOrDie returns the type of the data dir.
// Dies if the datadir is invalid.
func identifyDataDirOrDie(dir string) dirType {
	names, err := fileutil.ReadDir(dir)
	if err != nil {
		if os.IsNotExist(err) {
			return dirEmpty
		}
		plog.Fatalf("error listing data dir: %s", dir)
	}

	var m, p bool
	for _, name := range names {
		switch dirType(name) {
		case dirMember:
			m = true
		case dirProxy:
			p = true
		default:
			plog.Warningf("found invalid file/dir %s under data dir %s (Ignore this if you are upgrading etcd)", name, dir)
		}
	}

	if m && p {
		plog.Fatal("invalid datadir. Both member and proxy directories exist.")
	}
	if m {
		return dirMember
	}
	if p {
		return dirProxy
	}
	return dirEmpty
}

func setupLogging(cfg *config) {
	capnslog.SetGlobalLogLevel(capnslog.INFO)
	if cfg.debug {
		capnslog.SetGlobalLogLevel(capnslog.DEBUG)
	}
	if cfg.logPkgLevels != "" {
		repoLog := capnslog.MustRepoLogger("github.com/coreos/etcd")
		settings, err := repoLog.ParseLogLevelConfig(cfg.logPkgLevels)
		if err != nil {
			plog.Warningf("couldn't parse log level string: %s, continuing with default levels", err.Error())
			return
		}
		repoLog.SetLogLevel(settings)
	}
}
<commit_msg>etcdmain: exit if discovery fails<commit_after>// Copyright 2015 CoreOS, 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 etcdmain

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path"
	"reflect"
	"runtime"
	"strconv"
	"time"

	"github.com/coreos/etcd/discovery"
	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/etcd/pkg/cors"
	"github.com/coreos/etcd/pkg/fileutil"
	"github.com/coreos/etcd/pkg/osutil"
	"github.com/coreos/etcd/pkg/transport"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/proxy"
	"github.com/coreos/etcd/rafthttp"

	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
)

type dirType string

var plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdmain")

const (
	// the owner can make/remove files inside the directory
	privateDirMode = 0700
)

var (
	dirMember = dirType("member")
	dirProxy  = dirType("proxy")
	dirEmpty  = dirType("empty")
)

func Main() {
	capnslog.SetFormatter(capnslog.NewStringFormatter(os.Stderr))
	cfg := NewConfig()
	err := cfg.Parse(os.Args[1:])
	if err != nil {
		plog.Errorf("error verifying flags, %v. See 'etcd --help'.", err)
		switch err {
		case errUnsetAdvertiseClientURLsFlag:
			plog.Errorf("When listening on specific address(es), this etcd process must advertise accessible url(s) to each connected client.")
		}
		os.Exit(1)
	}
	setupLogging(cfg)

	var stopped <-chan struct{}

	GoMaxProcs := 1
	if envMaxProcs, err := strconv.Atoi(os.Getenv("GOMAXPROCS")); err == nil {
		GoMaxProcs = envMaxProcs
	}
	plog.Infof("setting maximum number of CPUs to %d, total number of available CPUs is %d", GoMaxProcs, runtime.NumCPU())
	runtime.GOMAXPROCS(GoMaxProcs)

	// TODO: check whether fields are set instead of whether fields have default value
	if cfg.name != defaultName && cfg.initialCluster == initialClusterFromName(defaultName) {
		cfg.initialCluster = initialClusterFromName(cfg.name)
	}

	if cfg.dir == "" {
		cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
		plog.Warningf("no data-dir provided, using default data-dir ./%s", cfg.dir)
	}

	which := identifyDataDirOrDie(cfg.dir)
	if which != dirEmpty {
		plog.Noticef("the server is already initialized as %v before, starting as etcd %v...", which, which)
	}

	shouldProxy := cfg.isProxy() || which == dirProxy
	if !shouldProxy {
		stopped, err = startEtcd(cfg)
		if err == discovery.ErrFullCluster && cfg.shouldFallbackToProxy() {
			plog.Noticef("discovery cluster full, falling back to %s", fallbackFlagProxy)
			shouldProxy = true
		}
	}
	if shouldProxy {
		err = startProxy(cfg)
	}
	if err != nil {
		switch err {
		case discovery.ErrDuplicateID:
			plog.Errorf("member %q has previously registered with discovery service token (%s).", cfg.name, cfg.durl)
			plog.Errorf("But etcd could not find vaild cluster configuration in the given data dir (%s).", cfg.dir)
			plog.Infof("Please check the given data dir path if the previous bootstrap succeeded")
			plog.Infof("or use a new discovery token if the previous bootstrap failed.")
			os.Exit(1)
		default:
			plog.Fatalf("%v", err)
		}
	}

	osutil.HandleInterrupts()

	<-stopped
	osutil.Exit(0)
}

// startEtcd launches the etcd server and HTTP handlers for client/server communication.
func startEtcd(cfg *config) (<-chan struct{}, error) {
	urlsmap, token, err := getPeerURLsMapAndToken(cfg)
	if err != nil {
		return nil, fmt.Errorf("error setting up initial cluster: %v", err)
	}

	pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, rafthttp.DialTimeout, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
	if err != nil {
		return nil, err
	}

	if !cfg.peerTLSInfo.Empty() {
		plog.Infof("peerTLS: %s", cfg.peerTLSInfo)
	}
	plns := make([]net.Listener, 0)
	for _, u := range cfg.lpurls {
		var l net.Listener
		l, err = transport.NewTimeoutListener(u.Host, u.Scheme, cfg.peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
		if err != nil {
			return nil, err
		}

		urlStr := u.String()
		plog.Info("listening for peers on ", urlStr)
		defer func() {
			if err != nil {
				l.Close()
				plog.Info("stopping listening for peers on ", urlStr)
			}
		}()
		plns = append(plns, l)
	}

	if !cfg.clientTLSInfo.Empty() {
		plog.Infof("clientTLS: %s", cfg.clientTLSInfo)
	}
	clns := make([]net.Listener, 0)
	for _, u := range cfg.lcurls {
		var l net.Listener
		l, err = transport.NewKeepAliveListener(u.Host, u.Scheme, cfg.clientTLSInfo)
		if err != nil {
			return nil, err
		}

		urlStr := u.String()
		plog.Info("listening for client requests on ", urlStr)
		defer func() {
			if err != nil {
				l.Close()
				plog.Info("stopping listening for client requests on ", urlStr)
			}
		}()
		clns = append(clns, l)
	}

	srvcfg := &etcdserver.ServerConfig{
		Name:                cfg.name,
		ClientURLs:          cfg.acurls,
		PeerURLs:            cfg.apurls,
		DataDir:             cfg.dir,
		SnapCount:           cfg.snapCount,
		MaxSnapFiles:        cfg.maxSnapFiles,
		MaxWALFiles:         cfg.maxWalFiles,
		InitialPeerURLsMap:  urlsmap,
		InitialClusterToken: token,
		DiscoveryURL:        cfg.durl,
		DiscoveryProxy:      cfg.dproxy,
		NewCluster:          cfg.isNewCluster(),
		ForceNewCluster:     cfg.forceNewCluster,
		Transport:           pt,
		TickMs:              cfg.TickMs,
		ElectionTicks:       cfg.electionTicks(),
	}
	var s *etcdserver.EtcdServer
	s, err = etcdserver.NewServer(srvcfg)
	if err != nil {
		return nil, err
	}
	s.Start()
	osutil.RegisterInterruptHandler(s.Stop)

	if cfg.corsInfo.String() != "" {
		plog.Infof("cors = %s", cfg.corsInfo)
	}
	ch := &cors.CORSHandler{
		Handler: etcdhttp.NewClientHandler(s),
		Info:    cfg.corsInfo,
	}
	ph := etcdhttp.NewPeerHandler(s.Cluster(), s.RaftHandler())
	// Start the peer server in a goroutine
	for _, l := range plns {
		go func(l net.Listener) {
			plog.Fatal(serveHTTP(l, ph, 5*time.Minute))
		}(l)
	}
	// Start a client server goroutine for each listen address
	for _, l := range clns {
		go func(l net.Listener) {
			// read timeout does not work with http close notify
			// TODO: https://github.com/golang/go/issues/9524
			plog.Fatal(serveHTTP(l, ch, 0))
		}(l)
	}
	return s.StopNotify(), nil
}

// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
func startProxy(cfg *config) error {
	urlsmap, _, err := getPeerURLsMapAndToken(cfg)
	if err != nil {
		return fmt.Errorf("error setting up initial cluster: %v", err)
	}

	if cfg.durl != "" {
		s, err := discovery.GetCluster(cfg.durl, cfg.dproxy)
		if err != nil {
			return err
		}
		if urlsmap, err = types.NewURLsMap(s); err != nil {
			return err
		}
	}

	pt, err := transport.NewTransport(cfg.clientTLSInfo)
	pt.MaxIdleConnsPerHost = proxy.DefaultMaxIdleConnsPerHost
	if err != nil {
		return err
	}

	tr, err := transport.NewTransport(cfg.peerTLSInfo)
	if err != nil {
		return err
	}

	cfg.dir = path.Join(cfg.dir, "proxy")
	err = os.MkdirAll(cfg.dir, 0700)
	if err != nil {
		return err
	}

	var peerURLs []string
	clusterfile := path.Join(cfg.dir, "cluster")

	b, err := ioutil.ReadFile(clusterfile)
	switch {
	case err == nil:
		urls := struct{ PeerURLs []string }{}
		err := json.Unmarshal(b, &urls)
		if err != nil {
			return err
		}
		peerURLs = urls.PeerURLs
		plog.Infof("proxy: using peer urls %v from cluster file ./%s", peerURLs, clusterfile)
	case os.IsNotExist(err):
		peerURLs = urlsmap.URLs()
		plog.Infof("proxy: using peer urls %v ", peerURLs)
	default:
		return err
	}

	clientURLs := []string{}
	uf := func() []string {
		gcls, err := etcdserver.GetClusterFromRemotePeers(peerURLs, tr)
		// TODO: remove the 2nd check when we fix GetClusterFromPeers
		// GetClusterFromPeers should not return nil error with an invaild empty cluster
		if err != nil {
			plog.Warningf("proxy: %v", err)
			return []string{}
		}
		if len(gcls.Members()) == 0 {
			return clientURLs
		}
		clientURLs = gcls.ClientURLs()

		urls := struct{ PeerURLs []string }{gcls.PeerURLs()}
		b, err := json.Marshal(urls)
		if err != nil {
			plog.Warningf("proxy: error on marshal peer urls %s", err)
			return clientURLs
		}

		err = ioutil.WriteFile(clusterfile+".bak", b, 0600)
		if err != nil {
			plog.Warningf("proxy: error on writing urls %s", err)
			return clientURLs
		}
		err = os.Rename(clusterfile+".bak", clusterfile)
		if err != nil {
			plog.Warningf("proxy: error on updating clusterfile %s", err)
			return clientURLs
		}
		if !reflect.DeepEqual(gcls.PeerURLs(), peerURLs) {
			plog.Noticef("proxy: updated peer urls in cluster file from %v to %v", peerURLs, gcls.PeerURLs())
		}
		peerURLs = gcls.PeerURLs()

		return clientURLs
	}
	ph := proxy.NewHandler(pt, uf)
	ph = &cors.CORSHandler{
		Handler: ph,
		Info:    cfg.corsInfo,
	}

	if cfg.isReadonlyProxy() {
		ph = proxy.NewReadonlyHandler(ph)
	}
	// Start a proxy server goroutine for each listen address
	for _, u := range cfg.lcurls {
		l, err := transport.NewListener(u.Host, u.Scheme, cfg.clientTLSInfo)
		if err != nil {
			return err
		}

		host := u.Host
		go func() {
			plog.Info("proxy: listening for client requests on ", host)
			plog.Fatal(http.Serve(l, ph))
		}()
	}
	return nil
}

// getPeerURLsMapAndToken sets up an initial peer URLsMap and cluster token for bootstrap or discovery.
func getPeerURLsMapAndToken(cfg *config) (urlsmap types.URLsMap, token string, err error) {
	switch {
	case cfg.durl != "":
		urlsmap = types.URLsMap{}
		// If using discovery, generate a temporary cluster based on
		// self's advertised peer URLs
		urlsmap[cfg.name] = cfg.apurls
		token = cfg.durl
	case cfg.dnsCluster != "":
		var clusterStr string
		clusterStr, token, err = discovery.SRVGetCluster(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls)
		if err != nil {
			return nil, "", err
		}
		urlsmap, err = types.NewURLsMap(clusterStr)
	default:
		// We're statically configured, and cluster has appropriately been set.
		urlsmap, err = types.NewURLsMap(cfg.initialCluster)
		token = cfg.initialClusterToken
	}
	return urlsmap, token, err
}

// identifyDataDirOrDie returns the type of the data dir.
// Dies if the datadir is invalid.
func identifyDataDirOrDie(dir string) dirType {
	names, err := fileutil.ReadDir(dir)
	if err != nil {
		if os.IsNotExist(err) {
			return dirEmpty
		}
		plog.Fatalf("error listing data dir: %s", dir)
	}

	var m, p bool
	for _, name := range names {
		switch dirType(name) {
		case dirMember:
			m = true
		case dirProxy:
			p = true
		default:
			plog.Warningf("found invalid file/dir %s under data dir %s (Ignore this if you are upgrading etcd)", name, dir)
		}
	}

	if m && p {
		plog.Fatal("invalid datadir. Both member and proxy directories exist.")
	}
	if m {
		return dirMember
	}
	if p {
		return dirProxy
	}
	return dirEmpty
}

func setupLogging(cfg *config) {
	capnslog.SetGlobalLogLevel(capnslog.INFO)
	if cfg.debug {
		capnslog.SetGlobalLogLevel(capnslog.DEBUG)
	}
	if cfg.logPkgLevels != "" {
		repoLog := capnslog.MustRepoLogger("github.com/coreos/etcd")
		settings, err := repoLog.ParseLogLevelConfig(cfg.logPkgLevels)
		if err != nil {
			plog.Warningf("couldn't parse log level string: %s, continuing with default levels", err.Error())
			return
		}
		repoLog.SetLogLevel(settings)
	}
}
<|endoftext|>


<commit_before>package sqltest

import (
	"database/sql/driver"
	"errors"
	"io"

	"github.com/reflexionhealth/vanilla/sql/sqltest/ast"
	"github.com/reflexionhealth/vanilla/sql/sqltest/parser"
)

type Driver struct{}

func (d *Driver) Open(name string) (driver.Conn, error) {
	return new(Conn), nil
}

type Conn struct {
	Closed bool
}

func (c *Conn) Prepare(query string) (driver.Stmt, error) {
	prep := parser.Make([]byte(query), parser.Ruleset{})
	stmt, err := prep.ParseStatement()
	return &Stmt{Ast: stmt}, err
}

func (c *Conn) Close() error {
	// TODO: Return an error if not all Rows created by the connection have been closed
	c.Closed = true
	return nil
}

func (c *Conn) Begin() (driver.Tx, error) {
	return nil, errors.New("TODO: Implement Conn.Begin() for testing of transactions")
}

type Stmt struct {
	Ast    ast.Stmt
	Closed bool
}

func (s *Stmt) Close() error {
	s.Closed = true
	return nil
}

func (s *Stmt) NumInput() int {
	return -1
}

func (s *Stmt) Exec(args []driver.Value) (driver.Result, error) {
	return nil, errors.New("TODO: Implement Stmt.Exec() for testing of INSERTs, UPDATEs")
}

func (s *Stmt) Query(args []driver.Value) (driver.Rows, error) {
	slct, ok := s.Ast.(*ast.SelectStmt)
	if !ok {
		return nil, errors.New("called Query() but statement is not a SELECT")
	}

	var columns []string
	for _, expr := range slct.Selection {
		if ident, ok := expr.(*ast.Identifier); ok {
			columns = append(columns, ident.Name)
		} else {
			columns = append(columns, "")
		}
	}

	return &Rows{columns: columns}, nil
}

type Rows struct {
	Closed  bool
	Scanned int // count of scanned rows

	columns []string
	rows    [][]driver.Value
}

func (r *Rows) Columns() []string {
	return r.columns
}

func (r *Rows) Close() error {
	r.Closed = true
	return nil
}

func (r *Rows) Next(dest []driver.Value) error {
	if r.Scanned < len(r.rows) {
		src := r.rows[r.Scanned]
		for i := range src {
			dest[i] = src[i]
		}
		r.Scanned += 1
		return nil
	} else {
		return io.EOF
	}
}
<commit_msg>sql/sqltest: specify ruleset when registering driver<commit_after>package sqltest

import (
	"database/sql"
	"database/sql/driver"
	"errors"
	"io"

	"github.com/reflexionhealth/vanilla/sql/sqltest/ast"
	"github.com/reflexionhealth/vanilla/sql/sqltest/parser"
	"github.com/reflexionhealth/vanilla/sql/sqltest/scanner"
)

var AnsiRuleset = parser.Ruleset{}
var MysqlRuleset = parser.Ruleset{
	CanSelectDistinctRow: true,
	ScanRules: scanner.Ruleset{
		BacktickIsQuotemark:       true,
		DoubleQuoteIsNotQuotemark: true,
	},
}

func Register(name string, rules parser.Ruleset) {
	sql.Register(name, &Driver{rules})
}

type Driver struct {
	Rules parser.Ruleset
}

func (d *Driver) Open(name string) (driver.Conn, error) {
	return &Conn{Rules: d.Rules}, nil
}

type Conn struct {
	Closed bool
	Rules  parser.Ruleset
}

func (c *Conn) Prepare(query string) (driver.Stmt, error) {
	prep := parser.Make([]byte(query), c.Rules)
	stmt, err := prep.ParseStatement()
	return &Stmt{Ast: stmt}, err
}

func (c *Conn) Close() error {
	// TODO: Return an error if not all Rows created by the connection have been closed
	c.Closed = true
	return nil
}

func (c *Conn) Begin() (driver.Tx, error) {
	return nil, errors.New("TODO: Implement Conn.Begin() for testing of transactions")
}

type Stmt struct {
	Closed bool
	Ast    ast.Stmt
}

func (s *Stmt) Close() error {
	s.Closed = true
	return nil
}

func (s *Stmt) NumInput() int {
	return -1
}

func (s *Stmt) Exec(args []driver.Value) (driver.Result, error) {
	return nil, errors.New("TODO: Implement Stmt.Exec() for testing of INSERTs, UPDATEs")
}

func (s *Stmt) Query(args []driver.Value) (driver.Rows, error) {
	slct, ok := s.Ast.(*ast.SelectStmt)
	if !ok {
		return nil, errors.New("called Query() but statement is not a SELECT")
	}

	var columns []string
	for _, expr := range slct.Selection {
		if ident, ok := expr.(*ast.Identifier); ok {
			columns = append(columns, ident.Name)
		} else {
			columns = append(columns, "")
		}
	}

	return &Rows{columns: columns}, nil
}

type Rows struct {
	Closed  bool
	Scanned int // count of scanned rows

	columns []string
	rows    [][]driver.Value
}

func (r *Rows) Columns() []string {
	return r.columns
}

func (r *Rows) Close() error {
	r.Closed = true
	return nil
}

func (r *Rows) Next(dest []driver.Value) error {
	if r.Scanned < len(r.rows) {
		src := r.rows[r.Scanned]
		for i := range src {
			dest[i] = src[i]
		}
		r.Scanned += 1
		return nil
	} else {
		return io.EOF
	}
}
<|endoftext|>


<commit_before>package project_test

import (
	"context"
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/db/cluster"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/shared/api"
)

// If there's no limit configured on the project, the check passes.
func TestAllowInstanceCreation_NotConfigured(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	req := api.InstancesPost{
		Name: "c1",
		Type: api.InstanceTypeContainer,
	}
	err := project.AllowInstanceCreation(tx, "default", req)
	assert.NoError(t, err)
}

// If a limit is configured and the current number of instances is below it, the check passes.
func TestAllowInstanceCreation_Below(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"limits.containers": "5"})
	require.NoError(t, err)

	_, err = cluster.CreateInstance(ctx, tx.Tx(), cluster.Instance{
		Project:      "p1",
		Name:         "c1",
		Type:         instancetype.Container,
		Architecture: 1,
		Node:         "none",
	})
	require.NoError(t, err)

	req := api.InstancesPost{
		Name: "c2",
		Type: api.InstanceTypeContainer,
	}

	err = project.AllowInstanceCreation(tx, "p1", req)
	assert.NoError(t, err)
}

// If a limit is configured and it matches the current number of instances, the
// check fails.
func TestAllowInstanceCreation_Above(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"limits.containers": "1"})
	require.NoError(t, err)

	_, err = cluster.CreateInstance(ctx, tx.Tx(), cluster.Instance{
		Project:      "p1",
		Name:         "c1",
		Type:         instancetype.Container,
		Architecture: 1,
		Node:         "none",
	})
	require.NoError(t, err)

	req := api.InstancesPost{
		Name: "c2",
		Type: api.InstanceTypeContainer,
	}

	err = project.AllowInstanceCreation(tx, "p1", req)
	assert.EqualError(t, err, `Reached maximum number of instances of type "container" in project "p1"`)
}

// If a limit is configured, but for a different instance type, the check
// passes.
func TestAllowInstanceCreation_DifferentType(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"limits.containers": "1"})
	require.NoError(t, err)

	_, err = cluster.CreateInstance(ctx, tx.Tx(), cluster.Instance{
		Project:      "p1",
		Name:         "vm1",
		Type:         instancetype.VM,
		Architecture: 1,
		Node:         "none",
	})
	require.NoError(t, err)

	req := api.InstancesPost{
		Name: "c2",
		Type: api.InstanceTypeContainer,
	}

	err = project.AllowInstanceCreation(tx, "p1", req)
	assert.NoError(t, err)
}

// If a limit is configured, but the limit on instances is more
// restrictive, the check fails
func TestAllowInstanceCreation_AboveInstances(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"limits.containers": "5", "limits.instances": "1"})
	require.NoError(t, err)

	_, err = cluster.CreateInstance(ctx, tx.Tx(), cluster.Instance{
		Project:      "p1",
		Name:         "c1",
		Type:         instancetype.Container,
		Architecture: 1,
		Node:         "none",
	})
	require.NoError(t, err)

	req := api.InstancesPost{
		Name: "c2",
		Type: api.InstanceTypeContainer,
	}

	err = project.AllowInstanceCreation(tx, "p1", req)
	assert.EqualError(t, err, `Reached maximum number of instances in project "p1"`)
}

// If a direct targeting is blocked, the check fails.
func TestCheckClusterTargetRestriction_RestrictedTrue(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"restricted": "true", "restricted.cluster.target": "block"})
	require.NoError(t, err)

	dbProject, err := cluster.GetProject(ctx, tx.Tx(), "p1")
	require.NoError(t, err)

	p, err := dbProject.ToAPI(ctx, tx.Tx())
	require.NoError(t, err)

	req := &http.Request{}

	err = project.CheckClusterTargetRestriction(tx, req, p, "n1")
	assert.EqualError(t, err, "This project doesn't allow cluster member targeting")
}

// If a direct targeting is allowed, the check passes.
func TestCheckClusterTargetRestriction_RestrictedFalse(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"restricted": "false", "restricted.cluster.target": "block"})
	require.NoError(t, err)

	dbProject, err := cluster.GetProject(ctx, tx.Tx(), "p1")
	require.NoError(t, err)

	p, err := dbProject.ToAPI(ctx, tx.Tx())
	require.NoError(t, err)

	req := &http.Request{}

	err = project.CheckClusterTargetRestriction(tx, req, p, "n1")
	assert.NoError(t, err)
}
<commit_msg>lxd/project: Ends all comments with a full-stop.<commit_after>package project_test

import (
	"context"
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/db/cluster"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/shared/api"
)

// If there's no limit configured on the project, the check passes.
func TestAllowInstanceCreation_NotConfigured(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	req := api.InstancesPost{
		Name: "c1",
		Type: api.InstanceTypeContainer,
	}
	err := project.AllowInstanceCreation(tx, "default", req)
	assert.NoError(t, err)
}

// If a limit is configured and the current number of instances is below it, the check passes.
func TestAllowInstanceCreation_Below(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"limits.containers": "5"})
	require.NoError(t, err)

	_, err = cluster.CreateInstance(ctx, tx.Tx(), cluster.Instance{
		Project:      "p1",
		Name:         "c1",
		Type:         instancetype.Container,
		Architecture: 1,
		Node:         "none",
	})
	require.NoError(t, err)

	req := api.InstancesPost{
		Name: "c2",
		Type: api.InstanceTypeContainer,
	}

	err = project.AllowInstanceCreation(tx, "p1", req)
	assert.NoError(t, err)
}

// If a limit is configured and it matches the current number of instances, the
// check fails.
func TestAllowInstanceCreation_Above(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"limits.containers": "1"})
	require.NoError(t, err)

	_, err = cluster.CreateInstance(ctx, tx.Tx(), cluster.Instance{
		Project:      "p1",
		Name:         "c1",
		Type:         instancetype.Container,
		Architecture: 1,
		Node:         "none",
	})
	require.NoError(t, err)

	req := api.InstancesPost{
		Name: "c2",
		Type: api.InstanceTypeContainer,
	}

	err = project.AllowInstanceCreation(tx, "p1", req)
	assert.EqualError(t, err, `Reached maximum number of instances of type "container" in project "p1"`)
}

// If a limit is configured, but for a different instance type, the check
// passes.
func TestAllowInstanceCreation_DifferentType(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"limits.containers": "1"})
	require.NoError(t, err)

	_, err = cluster.CreateInstance(ctx, tx.Tx(), cluster.Instance{
		Project:      "p1",
		Name:         "vm1",
		Type:         instancetype.VM,
		Architecture: 1,
		Node:         "none",
	})
	require.NoError(t, err)

	req := api.InstancesPost{
		Name: "c2",
		Type: api.InstanceTypeContainer,
	}

	err = project.AllowInstanceCreation(tx, "p1", req)
	assert.NoError(t, err)
}

// If a limit is configured, but the limit on instances is more
// restrictive, the check fails.
func TestAllowInstanceCreation_AboveInstances(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"limits.containers": "5", "limits.instances": "1"})
	require.NoError(t, err)

	_, err = cluster.CreateInstance(ctx, tx.Tx(), cluster.Instance{
		Project:      "p1",
		Name:         "c1",
		Type:         instancetype.Container,
		Architecture: 1,
		Node:         "none",
	})
	require.NoError(t, err)

	req := api.InstancesPost{
		Name: "c2",
		Type: api.InstanceTypeContainer,
	}

	err = project.AllowInstanceCreation(tx, "p1", req)
	assert.EqualError(t, err, `Reached maximum number of instances in project "p1"`)
}

// If a direct targeting is blocked, the check fails.
func TestCheckClusterTargetRestriction_RestrictedTrue(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"restricted": "true", "restricted.cluster.target": "block"})
	require.NoError(t, err)

	dbProject, err := cluster.GetProject(ctx, tx.Tx(), "p1")
	require.NoError(t, err)

	p, err := dbProject.ToAPI(ctx, tx.Tx())
	require.NoError(t, err)

	req := &http.Request{}

	err = project.CheckClusterTargetRestriction(tx, req, p, "n1")
	assert.EqualError(t, err, "This project doesn't allow cluster member targeting")
}

// If a direct targeting is allowed, the check passes.
func TestCheckClusterTargetRestriction_RestrictedFalse(t *testing.T) {
	tx, cleanup := db.NewTestClusterTx(t)
	defer cleanup()

	ctx := context.Background()
	id, err := cluster.CreateProject(ctx, tx.Tx(), cluster.Project{Name: "p1"})
	require.NoError(t, err)

	err = cluster.CreateProjectConfig(ctx, tx.Tx(), id, map[string]string{"restricted": "false", "restricted.cluster.target": "block"})
	require.NoError(t, err)

	dbProject, err := cluster.GetProject(ctx, tx.Tx(), "p1")
	require.NoError(t, err)

	p, err := dbProject.ToAPI(ctx, tx.Tx())
	require.NoError(t, err)

	req := &http.Request{}

	err = project.CheckClusterTargetRestriction(tx, req, p, "n1")
	assert.NoError(t, err)
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"go.mongodb.org/mongo-driver/bson"

	"github.com/256dpi/fire/axe"
	"github.com/256dpi/fire/coal"
	"github.com/256dpi/fire/glut"
)

func incrementTask(store *coal.Store) *axe.Task {
	return &axe.Task{
		Name:  "increment",
		Model: &count{},
		Handler: func(ctx *axe.Context) error {
			// increment count
			_, err := store.C(&Item{}).UpdateOne(ctx, bson.M{
				"_id": ctx.Model.(*count).Item,
			}, bson.M{
				"$inc": bson.M{
					coal.F(&Item{}, "Count"): 1,
				},
			})
			if err != nil {
				return err
			}

			return nil
		},
	}
}

func periodicTask(store *coal.Store) *axe.Task {
	return &axe.Task{
		Name:  "periodic",
		Model: nil,
		Handler: func(ctx *axe.Context) error {
			// increment value
			err := glut.Mut(ctx, store, "periodic", "counter", 0, func(ok bool, data coal.Map) (coal.Map, error) {
				if !ok {
					data = coal.Map{"n": int64(1)}
				}
				data["n"] = data["n"].(int64) + 1
				return data, nil
			})
			if err != nil {
				return err
			}

			return nil
		},
		Periodically: 5 * time.Second,
		PeriodicJob: axe.Blueprint{
			Label: "periodic",
		},
	}
}
<commit_msg>use manager in example<commit_after>package main

import (
	"time"

	"go.mongodb.org/mongo-driver/bson"

	"github.com/256dpi/fire/axe"
	"github.com/256dpi/fire/coal"
	"github.com/256dpi/fire/glut"
)

func incrementTask(store *coal.Store) *axe.Task {
	return &axe.Task{
		Name:  "increment",
		Model: &count{},
		Handler: func(ctx *axe.Context) error {
			// get id
			id := ctx.Model.(*count).Item

			// increment count
			_, err := store.M(&Item{}).Update(ctx, id, bson.M{
				"$inc": bson.M{
					"Count": 1,
				},
			})
			if err != nil {
				return err
			}

			return nil
		},
	}
}

func periodicTask(store *coal.Store) *axe.Task {
	return &axe.Task{
		Name:  "periodic",
		Model: nil,
		Handler: func(ctx *axe.Context) error {
			// increment value
			err := glut.Mut(ctx, store, "periodic", "counter", 0, func(ok bool, data coal.Map) (coal.Map, error) {
				if !ok {
					data = coal.Map{"n": int64(1)}
				}
				data["n"] = data["n"].(int64) + 1
				return data, nil
			})
			if err != nil {
				return err
			}

			return nil
		},
		Periodically: 5 * time.Second,
		PeriodicJob: axe.Blueprint{
			Label: "periodic",
		},
	}
}
<|endoftext|>


<commit_before>package main

import "github.com/mithereal/go-ebay"
import "github.com/davecgh/go-spew/spew"
import "github.com/franela/goreq"
import "github.com/alecthomas/colour"
import "gopkg.in/alecthomas/kingpin.v2"
import "encoding/xml"
import "bytes"

const (

        Version = "1.0"
		Token = ""

)

var (
    verbose     = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
	listingtype = kingpin.Flag("listingtype", "Type of listing (Auction).").Short('l').String()

	createtimefrom = kingpin.Flag("createtimefrom", "Time the first order was created.").Short('s').String()
	createtimeto   = kingpin.Flag("createtimeto", "Time the last order was created.").Short('e').String()
)

func main() {

 kingpin.Parse()

 OrdersRequest := &ebay.GetOrdersRequest  {
  		Xmlns:          "urn:ebay:apis:eBLBaseComponents",
		CreateTimeFrom: "2007-12-01T20:34:44.000Z",
		CreateTimeTo:   "2007-12-10T20:34:44.000Z",
		OrderRole:      "Seller",
		OrderStatus:    "Completed",
		Version:        "933",
   }

OrdersRequest.RequesterCredentials.SetToken(Token)

xml,_ := xml.Marshal(OrdersRequest)
body := bytes.NewBuffer(xml)

req := goreq.Request{
	Method: "POST",
	Uri: "https://api.ebay.com/ws/api.dll",
    Body: body,
    ContentType: "application/xml; charset=utf-8",
    UserAgent: "go-ebay-fetch-orders",
    ShowDebug:   true,
	}

	req.AddHeader("Accept", "application/xml,application/xhtml+xml")
	req.AddHeader("X-Powered-By", "go-ebay (https://goo.gl/Zi7RMK)")
	req.AddHeader("X-Author", "Jason Clark (mithereal@gmail.com)")

	response, err := req.Do()

	if err != nil {
		colour.Println("ERROR - processUrl -> req.Do: " + err.Error())
		return
	}

spew.Dump(response.Body.ToString())

		}

<commit_msg>updated example<commit_after>package main

import "github.com/mithereal/go-ebay"
import "github.com/davecgh/go-spew/spew"
import "github.com/franela/goreq"
import "github.com/alecthomas/colour"
import "gopkg.in/alecthomas/kingpin.v2"
import "encoding/xml"
import "bytes"

const (

        Version = "1.0"
		EbayApiVersion = "933"
		EbayAppId =""
		Token = ""

)

var (
    verbose     = kingpin.Flag("verbose", "Verbose mode.").Short('v').Bool()
	listingtype = kingpin.Flag("listingtype", "Type of listing (Auction).").Short('l').String()

	createtimefrom = kingpin.Flag("createtimefrom", "Time the first order was created.").Short('s').String()
	createtimeto   = kingpin.Flag("createtimeto", "Time the last order was created.").Short('e').String()
)

func main() {

 kingpin.Parse()

 OrdersRequest := &ebay.GetOrdersRequest  {
  		Xmlns:          "urn:ebay:apis:eBLBaseComponents",
		CreateTimeFrom: "2007-12-01T20:34:44.000Z",
		CreateTimeTo:   "2007-12-10T20:34:44.000Z",
		OrderRole:      "Seller",
		OrderStatus:    "Completed",
		Version:        EbayApiVersion,
   }

OrdersRequest.RequesterCredentials.SetToken(Token)

xml,_ := xml.Marshal(OrdersRequest)
body := bytes.NewBuffer(xml)

req := goreq.Request{
	Method: "POST",
	Uri: "https://api.ebay.com/ws/api.dll",
    Body: body,
    ContentType: "application/xml; charset=utf-8",
    UserAgent: "go-ebay-fetch-orders",
    ShowDebug:   true,
	}

	req.AddHeader("X-EBAY-API-CALL-NAME", "GetOrdersRequest")
	req.AddHeader("X-EBAY-API-APP-ID", EbayAppId)
	req.AddHeader("X-EBAY-API-VERSION", EbayApiVersion)
	req.AddHeader("X-EBAY-API-REQUEST-ENCODING", "XML")
	req.AddHeader("X-EBAY-API-RESPONSE-ENCODING", "XML")
	req.AddHeader("X-EBAY-API-SITE-ID", "0")

	req.AddHeader("Accept", "application/xml,application/xhtml+xml")
	req.AddHeader("X-Powered-By", "go-ebay (https://goo.gl/Zi7RMK)")
	req.AddHeader("X-Author", "Jason Clark (mithereal@gmail.com)")

	response, err := req.Do()

	if err != nil {
		colour.Println("ERROR - processUrl -> req.Do: " + err.Error())
		return
	}

spew.Dump(response.Body.ToString())

		}

<|endoftext|>


<commit_before>package main

import (
	"time"

	"github.com/rakyll/go-firmata"
)

var led uint8 = 13

func main() {
	arduino, err := firmata.NewClient("/dev/cu.usbmodem1421", 57600)
	if err != nil {
		panic(err)
	}

	myDelay := time.Millisecond * 250

	arduino.SetPinMode(led, firmata.Output)
	for x := 0; x < 10; x++ {
		// Turn ON led
		arduino.DigitalWrite(led, true)
		time.Sleep(myDelay)
		// Turn OFF led
		arduino.DigitalWrite(led, false)
		time.Sleep(myDelay)
	}
	arduino.Close()
}
<commit_msg>Toggle forever.<commit_after>package main

import (
	"time"

	"github.com/rakyll/go-firmata"
)

var led uint8 = 13

func main() {
	arduino, err := firmata.NewClient("/dev/cu.usbmodem1421", 57600)
	if err != nil {
		panic(err)
	}
	arduino.SetPinMode(led, firmata.Output)
	for {
		// Turn ON led
		arduino.DigitalWrite(led, true)
		time.Sleep(time.Millisecond * 250)
		// Turn OFF led
		arduino.DigitalWrite(led, false)
		time.Sleep(time.Millisecond * 250)
	}
}
<|endoftext|>


<commit_before>package walnut

import (
	"fmt"
	"strings"
)

const (
	_ErrInvalidIndent = "invalid indentation on line %d"
	_ErrInvalidKey    = "invalid key on line %d"
	_ErrInvalidValue  = "unrecognized value on line %d: %q"
)

type definition struct {
	key  string
	val  interface{}
	raw  string
	line int
}

// Generates a Config instance from a raw configuration file. Returns an
// error if the source contains a syntax error.
func Parse(in []byte) (Config, error) {
	defs, err := parse(in)
	if err != nil {
		return nil, err
	}

	m := make(map[string]interface{})
	for _, def := range defs {
		m[def.key] = def.val
	}

	return Config(m), nil
}

// Generates a set of definitions from a raw configuration file. Returns an
// error if the source contains a syntax error.
func parse(in []byte) ([]definition, error) {
	defs := make([]definition, 0)

	stack := make([]string, 0)
	levels := make([]string, 0)
	isLeaf := false

	lines := strings.Split(string(in), "\n")

	for i, line := range lines {
		if isEmpty(line) {
			continue
		}

		s, k, v := components(line)
		d := depth(levels, s)

		if d < 0 || (d == len(levels) && isLeaf) {
			return nil, fmt.Errorf(_ErrInvalidIndent, i+1)
		}

		// trim redundant indentation levels
		if d < len(levels) {
			stack = stack[:d]
			levels = levels[:d]
		}

		stack = append(stack, k)
		levels = append(levels, s)

		// make sure the line specifies a valid key
		if k == "" {
			return nil, fmt.Errorf(_ErrInvalidKey, i+1)
		}

		// does the current line contain an assignment?
		if strings.ContainsRune(line, '=') {
			value, ok := literal(v)
			if !ok {
				return nil, fmt.Errorf(_ErrInvalidValue, i+1, v)
			}

			defs = append(defs, definition{
				key:  strings.Join(stack, "."),
				val:  value,
				raw:  v,
				line: i + 1,
			})

			isLeaf = true
			continue
		}

		isLeaf = false
	}

	return defs, nil
}

// Splits a line into three components: 1) leading whitespace, 2) a key,
// and optionally 3) a raw value.
//
//   components("  foo = bar")  // -> "  ", "foo", "bar"
//   components("foo")          // -> "", "foo", ""
func components(line string) (space, key, value string) {
	for i := 0; i < len(line); i++ {
		if line[i] != ' ' && line[i] != '\t' {
			break
		}
		space = string(line[:i+1])
	}

	if eq := strings.IndexRune(line, '='); eq != -1 {
		key = strings.TrimRight(line[len(space):eq], " \t")
		value = strings.TrimLeft(line[eq+1:], " \t")
	} else {
		key = strings.TrimRight(line[len(space):], " \t")
	}

	return
}

// Traverses a slice of previous indentation levels to see where the subject
// indentation fits in. Returns an integer between 0 and len(context) on
// success, or -1 if subject is not a valid indentation level in this context.
func depth(context []string, subject string) int {
	if subject == "" {
		return 0
	}

	// non-empty indentation without any context is illegal
	if len(context) == 0 {
		return -1
	}

	for i, previous := range context {
		if !strings.HasPrefix(subject, previous) {
			return -1
		}
		if len(subject) == len(previous) {
			return i
		}
	}

	// the subject line is further indented than its parent
	return len(context)
}

// Tries to extract a literal value from a string.
func literal(s string) (interface{}, bool) {
	if v, n := readBool(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readInt64(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readFloat64(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readString(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readTime(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readDuration(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}

	return nil, false
}

// Returns true if the line is completely made up of whitespace, or if the
// line contains only whitespace and a comment rune ('#').
func isEmpty(s string) bool {
	for i := 0; i < len(s); i++ {
		b := s[i]

		if b == ' ' || b == '\t' || b == '\r' {
			continue
		}
		if b == '#' {
			break
		}

		return false
	}

	return true
}
<commit_msg>Rename a bunch of variables in parse()<commit_after>package walnut

import (
	"fmt"
	"strings"
)

const (
	_ErrInvalidIndent = "invalid indentation on line %d"
	_ErrInvalidKey    = "invalid key on line %d"
	_ErrInvalidValue  = "unrecognized value on line %d: %q"
)

type definition struct {
	key  string
	val  interface{}
	raw  string
	line int
}

// Generates a Config instance from a raw configuration file. Returns an
// error if the source contains a syntax error.
func Parse(in []byte) (Config, error) {
	defs, err := parse(in)
	if err != nil {
		return nil, err
	}

	m := make(map[string]interface{})
	for _, def := range defs {
		m[def.key] = def.val
	}

	return Config(m), nil
}

// Generates a set of definitions from a raw configuration file. Returns an
// error if the source contains a syntax error.
func parse(in []byte) ([]definition, error) {
	lines := strings.Split(string(in), "\n")
	defs := make([]definition, 0)

	stack := make([]string, 0)
	levels := make([]string, 0)
	allowDeeper := true

	for i, line := range lines {
		if isEmpty(line) {
			continue
		}

		space, key, value := components(line)
		d := depth(levels, space)

		if d < 0 || (d == len(levels) && !allowDeeper) {
			return nil, fmt.Errorf(_ErrInvalidIndent, i+1)
		}

		// trim our history
		if d < len(levels) {
			stack = stack[:d]
			levels = levels[:d]
		}

		stack = append(stack, key)
		levels = append(levels, space)

		// make sure the line specifies a valid key
		if key == "" {
			return nil, fmt.Errorf(_ErrInvalidKey, i+1)
		}

		// does the current line contain an assignment?
		if strings.ContainsRune(line, '=') {
			parsed, ok := literal(value)
			if !ok {
				return nil, fmt.Errorf(_ErrInvalidValue, i+1, value)
			}

			defs = append(defs, definition{
				key:  strings.Join(stack, "."),
				val:  parsed,
				raw:  value,
				line: i + 1,
			})

			allowDeeper = false
			continue
		}

		allowDeeper = true
	}

	return defs, nil
}

// Splits a line into three components: 1) leading whitespace, 2) a key,
// and optionally 3) a raw value.
//
//   components("  foo = bar")  // -> "  ", "foo", "bar"
//   components("foo")          // -> "", "foo", ""
func components(line string) (space, key, value string) {
	for i := 0; i < len(line); i++ {
		if line[i] != ' ' && line[i] != '\t' {
			break
		}
		space = string(line[:i+1])
	}

	if eq := strings.IndexRune(line, '='); eq != -1 {
		key = strings.TrimRight(line[len(space):eq], " \t")
		value = strings.TrimLeft(line[eq+1:], " \t")
	} else {
		key = strings.TrimRight(line[len(space):], " \t")
	}

	return
}

// Traverses a slice of previous indentation levels to see where the subject
// indentation fits in. Returns an integer between 0 and len(context) on
// success, or -1 if subject is not a valid indentation level in this context.
func depth(context []string, subject string) int {
	if subject == "" {
		return 0
	}

	// non-empty indentation without any context is illegal
	if len(context) == 0 {
		return -1
	}

	for i, previous := range context {
		if !strings.HasPrefix(subject, previous) {
			return -1
		}
		if len(subject) == len(previous) {
			return i
		}
	}

	// the subject line is further indented than its parent
	return len(context)
}

// Tries to extract a literal value from a string.
func literal(s string) (interface{}, bool) {
	if v, n := readBool(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readInt64(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readFloat64(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readString(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readTime(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}
	if v, n := readDuration(s); n != 0 && isEmpty(s[n:]) {
		return v, true
	}

	return nil, false
}

// Returns true if the line is completely made up of whitespace, or if the
// line contains only whitespace and a comment rune ('#').
func isEmpty(s string) bool {
	for i := 0; i < len(s); i++ {
		b := s[i]

		if b == ' ' || b == '\t' || b == '\r' {
			continue
		}
		if b == '#' {
			break
		}

		return false
	}

	return true
}
<|endoftext|>


<commit_before>package objconv

import "time"

// The Parser interface must be implemented by types that provide decoding of a
// specific format (like json, resp, ...).
//
// Parsers are not expected to be safe for use by multiple goroutines.
type Parser interface {
	// ParseType is called by a decoder to ask the parser what is the type of
	// the next value that can be parsed.
	//
	// ParseType must be idempotent, it must be possible to call it multiple
	// without actually changing the state of the parser.
	ParseType() (Type, error)

	// ParseNil parses a nil value.
	ParseNil() error

	// ParseBool parses a boolean value.
	ParseBool() (bool, error)

	// ParseInt parses an integer value.
	ParseInt() (int64, error)

	// ParseBool parses an unsigned integer value.
	ParseUint() (uint64, error)

	// ParseBool parses a floating point value.
	ParseFloat() (float64, error)

	// ParseBool parses a string value.
	//
	// The string is returned as a byte slice because it is expected to be
	// pointing at an internal memory buffer, the decoder will make a copy of
	// the value. This design allows more memory allocation optimizations.
	ParseString() ([]byte, error)

	// ParseBool parses a byte array value.
	//
	// The returned byte slice is expected to be pointing at an internal memory
	// buffer, the decoder will make a copy of the value. This design allows more
	// memory allocation optimizations.
	ParseBytes() ([]byte, error)

	// ParseBool parses a time value.
	ParseTime() (time.Time, error)

	// ParseBool parses a duration value.
	ParseDuration() (time.Duration, error)

	// ParseError parses an error value.
	ParseError() (error, error)

	// ParseArrayBegin is called by the array-decoding algorithm when it starts.
	//
	// The method should return the length of the array being decoded, or a
	// negative value if it is unknown (some formats like json don't keep track
	// of the length of the array).
	ParseArrayBegin() (int, error)

	// ParseArrayEnd is called by the array-decoding algorithm when it
	// completes.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the array.
	ParseArrayEnd(int) error

	// ParseArrayNext is called by the array-decoding algorithm between each
	// value parsed in the array.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the array.
	//
	// If the ParseArrayBegin method returned a negative value this method
	// should return objconv.End to indicated that there is no more elements to
	// parse in the array. In this case the method is also called right before
	// decoding the first element ot handle the case where the array is empty
	// and the end-of-array marker can be read right away.
	ParseArrayNext(int) error

	// ParseMapBegin is called by the map-decoding algorithm when it starts.
	//
	// The method should return the length of the map being decoded, or a
	// negative value if it is unknown (some formats like json don't keep track
	// of the length of the map).
	ParseMapBegin() (int, error)

	// ParseMapEnd is called by the map-decoding algorithm when it completes.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the map.
	ParseMapEnd(int) error

	// ParseMapValue is called by the map-decoding algorithm after parsing a key
	// but before parsing the associated value.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the map.
	ParseMapValue(int) error

	// ParseMapNext is called by the map-decoding algorithm between each
	// value parsed in the map.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the map.
	//
	// If the ParseMapBegin method returned a negative value this method should
	// return objconv.End to indicated that there is no more elements to parse
	// in the map. In this case the method is also called right before decoding
	// the first element ot handle the case where the array is empty and the
	// end-of-map marker can be read right away.
	ParseMapNext(int) error
}

// The bytesDecoder interface may optionnaly be implemented by a Parser to
// provide an extra step in decoding a byte slice. This is sometimes necessary
// if the associated Emitter has transformed bytes slices because the format is
// not capable of representing binary data.
type bytesDecoder interface {
	// DecodeBytes is called when the destination variable for a string or a
	// byte slice is a byte slice, allowing the parser to apply a transformation
	// before the value is stored.
	DecodeBytes([]byte) ([]byte, error)
}

// The textParser interface may be implemented by parsers of human-readable
// formats. Such parsers instruct the encoder to prefer using
// encoding.TextUnmarshaler over encoding.BinaryUnmarshaler for example.
type textParser interface {
	// EmitsText returns true if the parser produces a human-readable format.
	TextParser() bool
}

func isTextParser(parser Parser) bool {
	p, _ := parser.(textParser)
	return p != nil && p.TextParser()
}
<commit_msg>docs: fix in parse<commit_after>package objconv

import "time"

// The Parser interface must be implemented by types that provide decoding of a
// specific format (like json, resp, ...).
//
// Parsers are not expected to be safe for use by multiple goroutines.
type Parser interface {
	// ParseType is called by a decoder to ask the parser what is the type of
	// the next value that can be parsed.
	//
	// ParseType must be idempotent, it must be possible to call it multiple
	// without actually changing the state of the parser.
	ParseType() (Type, error)

	// ParseNil parses a nil value.
	ParseNil() error

	// ParseBool parses a boolean value.
	ParseBool() (bool, error)

	// ParseInt parses an integer value.
	ParseInt() (int64, error)

	// ParseUint parses an unsigned integer value.
	ParseUint() (uint64, error)

	// ParseFloat parses a floating point value.
	ParseFloat() (float64, error)

	// ParseString parses a string value.
	//
	// The string is returned as a byte slice because it is expected to be
	// pointing at an internal memory buffer, the decoder will make a copy of
	// the value. This design allows more memory allocation optimizations.
	ParseString() ([]byte, error)

	// ParseBytes parses a byte array value.
	//
	// The returned byte slice is expected to be pointing at an internal memory
	// buffer, the decoder will make a copy of the value. This design allows more
	// memory allocation optimizations.
	ParseBytes() ([]byte, error)

	// ParseTime parses a time value.
	ParseTime() (time.Time, error)

	// ParseDuration parses a duration value.
	ParseDuration() (time.Duration, error)

	// ParseError parses an error value.
	ParseError() (error, error)

	// ParseArrayBegin is called by the array-decoding algorithm when it starts.
	//
	// The method should return the length of the array being decoded, or a
	// negative value if it is unknown (some formats like json don't keep track
	// of the length of the array).
	ParseArrayBegin() (int, error)

	// ParseArrayEnd is called by the array-decoding algorithm when it
	// completes.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the array.
	ParseArrayEnd(int) error

	// ParseArrayNext is called by the array-decoding algorithm between each
	// value parsed in the array.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the array.
	//
	// If the ParseArrayBegin method returned a negative value this method
	// should return objconv.End to indicated that there is no more elements to
	// parse in the array. In this case the method is also called right before
	// decoding the first element ot handle the case where the array is empty
	// and the end-of-array marker can be read right away.
	ParseArrayNext(int) error

	// ParseMapBegin is called by the map-decoding algorithm when it starts.
	//
	// The method should return the length of the map being decoded, or a
	// negative value if it is unknown (some formats like json don't keep track
	// of the length of the map).
	ParseMapBegin() (int, error)

	// ParseMapEnd is called by the map-decoding algorithm when it completes.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the map.
	ParseMapEnd(int) error

	// ParseMapValue is called by the map-decoding algorithm after parsing a key
	// but before parsing the associated value.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the map.
	ParseMapValue(int) error

	// ParseMapNext is called by the map-decoding algorithm between each
	// value parsed in the map.
	//
	// The method receives the iteration counter as argument, which indicates
	// how many values were decoded from the map.
	//
	// If the ParseMapBegin method returned a negative value this method should
	// return objconv.End to indicated that there is no more elements to parse
	// in the map. In this case the method is also called right before decoding
	// the first element ot handle the case where the array is empty and the
	// end-of-map marker can be read right away.
	ParseMapNext(int) error
}

// The bytesDecoder interface may optionnaly be implemented by a Parser to
// provide an extra step in decoding a byte slice. This is sometimes necessary
// if the associated Emitter has transformed bytes slices because the format is
// not capable of representing binary data.
type bytesDecoder interface {
	// DecodeBytes is called when the destination variable for a string or a
	// byte slice is a byte slice, allowing the parser to apply a transformation
	// before the value is stored.
	DecodeBytes([]byte) ([]byte, error)
}

// The textParser interface may be implemented by parsers of human-readable
// formats. Such parsers instruct the encoder to prefer using
// encoding.TextUnmarshaler over encoding.BinaryUnmarshaler for example.
type textParser interface {
	// EmitsText returns true if the parser produces a human-readable format.
	TextParser() bool
}

func isTextParser(parser Parser) bool {
	p, _ := parser.(textParser)
	return p != nil && p.TextParser()
}
<|endoftext|>


<commit_before>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-09-10
//  Update on 2014-10-31
//  Email  slowfei#foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"bytes"
	"fmt"
	"github.com/slowfei/gosfcore/utils/strings"
	"os"
	"regexp"
	"strings"
)

const (
	DOC_FILE_SUFFIX = ".dc"      // document file suffix(document comments)
	NIL_DOC_NAME    = "document" // nilDocParser struct use

)

var (

	//  主要用于去除注释的前缀
	_prefixFilterTags = [][]byte{
		[]byte(" *\t"),
		[]byte(" *  "),
		[]byte(" * "),
		[]byte("//\t"),
		[]byte("//  "),
		[]byte("// "),
		[]byte("//"),
	}

	_tagStar   = []byte("*")  // comments (*)
	_tagDSlash = []byte("//") // double slash
)

/**
 *	nil document parser
 *	specifically for .doc file serve
 */
type nilDocParser struct {
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) Name() string {
	return NIL_DOC_NAME
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) CheckFile(path string, info os.FileInfo) bool {
	return strings.HasSuffix(path, DOC_FILE_SUFFIX)
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) EachIndexFile(filebuf *FileBuf) {

}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParsePreview(filebuf *FileBuf) []Preview {
	return nil
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParseCodeblock(filebuf *FileBuf) []CodeBlock {
	return nil
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParsePackageInfo(filebuf *FileBuf) string {
	return ""
}

/**
 *	prese Preview Document CodeBlock array to markdown
 *
 *	@param `documents` after sorting
 *	@param `previews`  after sorting
 *	@param `blocks`	   after sorting
 *	@param `filesName` file names
 *	@param `relPath`   before code file name join path
 *	@return bytes
 */
func ParseMarkdown(documents []Document, previews []Preview, blocks []CodeBlock,
	filesName []string, relPath string) []byte {

	relPath = strings.TrimPrefix(relPath, "/")
	relPath = strings.TrimSuffix(relPath, "/")
	joinSymbol := ""
	if 0 != len(relPath) {
		joinSymbol = "/"
	}
	isWrite := false
	buf := bytes.NewBuffer([]byte{'\n'})

	for _, doc := range documents {
		isWrite = true
		// ## Welcome to gosfdoc
		// ------
		//
		// markdown syntax content
		//
		buf.WriteString("## " + doc.Title + "\n------\n")
		buf.WriteString(doc.Content)
		buf.WriteByte('\n')
	}

	if 0 != len(previews) {
		isWrite = true
		// ## Preview
		// ------
		// > [func Main()][#]

		// > [type TestStruct struct][#]

		// > implements: [Test][#]

		// >>[func (* TestStruct) hello(str string) string](#func_TestStruct.hello)

		// >>[func (* TestStruct) hello2() string][#]

		buf.WriteString("## Preview\n------\n")
		for _, pre := range previews {
			buf.WriteByte('\n')
			angle := ">"
			for i := 0; i
"
			}

			anchor := ""
			if 0 == len(pre.Anchor) {
				anchor = "(javascript:;)"
				// [show text](javascript:;)
			} else {
				anchor = fmt.Sprintf("(#f_%s)", pre.Anchor, pre.Anchor)
				// [show test](#f_anchor)

			}
			buf.WriteString(angle + " [" + pre.ShowText + "]" + anchor + "\n")

			if 0 != len(pre.DescText) {
				buf.WriteByte('\n')
				buf.WriteString(angle + " " + pre.DescText + "\n")
			}
		}
		buf.WriteByte('\n')
	}

	// out associate files
	if 0 != len(filesName) {
		isWrite = true
		// ###Package files
		// [a.go](#) [b.go](#) [c.go](#)
		buf.WriteString("
\n### Directory files\n")
		for _, name := range filesName {
			joinPath := relPath + joinSymbol + name
			buf.WriteString(fmt.Sprintf("[%s](%s) ", name, joinPath))
		}
		buf.WriteByte('\n')
	}

	if 0 != len(blocks) {
		isWrite = true
		buf.WriteByte('\n')
		isLinkCode := 0 != len(filesName)
		// ## Func Details
		// ------
		// ###[func (*TestStruct) hello](src.html?f=gosfdoc.go#L17)  [↩](#preview_TestStruct.hello)|[#](#func_TestStruct.hello)
		// > 函数介绍描述

		// >

		// > @param `str` 字符串传递

		// > @return `v1` 返回参数v1

		// > @return v2 返回参数v2

		//
		// ```go
		// func (* TestStruct) hello(str string) (v1,v2 string)
		// ```
		currentMenuTitle := ""

		for _, block := range blocks {

			if 0 != len(block.MenuTitle) && currentMenuTitle != block.MenuTitle {
				buf.WriteString("## " + block.MenuTitle + "\n------\n")
				currentMenuTitle = block.MenuTitle
			}

			if 0 != len(block.Title) {
				joinPath := "javascript:;"
				if isLinkCode && 0 != len(block.SourceFileName) {
					lineStr := ""

					lineLen := len(block.FileLines)
					if 1 == lineLen {
						lineStr = fmt.Sprintf("#L%d", block.FileLines[0])
					} else if 2 == lineLen {
						lineStr = fmt.Sprintf("#L%d-L%d", block.FileLines[0], block.FileLines[1])
					}

					joinPath = "src.html?f=" + relPath + joinSymbol + block.SourceFileName + lineStr
				}

				anchor := ""
				if 0 != len(block.Anchor) {
					anchor = fmt.Sprintf(" [↩](#p_%s) | [#](#f_%s)", block.Anchor, block.Anchor, block.Anchor)
				}

				buf.WriteString(fmt.Sprintf("### [%s](%s) %s\n", block.Title, joinPath, anchor))
			}

			if 0 != len(block.Desc) {
				//	content description
				descLines := strings.Split(block.Desc, "\n")
				for _, desc := range descLines {
					buf.WriteString(fmt.Sprintf("> %s
\n", desc))
				}
				buf.WriteByte('\n')
			}

			// code
			if 0 != len(block.Code) {
				buf.WriteString(fmt.Sprintf("\n```%s\n%s\n```\n\n", block.CodeLang, block.Code))
			}

			buf.WriteByte('\n')
		}
	}

	if isWrite {
		return buf.Bytes()
	} else {
		return nil
	}
}

/**
 *  parse public document content
 *
 *  @param `fileBuf`
 *  @return document array
 */
func ParseDocument(fileBuf *FileBuf) []Document {
	var resultDocs []Document = nil

	docsBuf := fileBuf.FinaAll(REXDocument)
	docsCount := len(docsBuf)

	if 0 == docsCount {
		return resultDocs
	}

	resultDocs = make([]Document, 0, docsCount)

	for i := 0; i < docsCount; i++ {
		docStruct := Document{}
		buf := docsBuf[i]

		lines := bytes.Split(buf, []byte("\n"))
		linesCount := len(lines)

		//  title and index parse
		indexTitleLine := lines[0]
		indexTitleMatch := REXDocIndexTitle.FindSubmatch(indexTitleLine)
		//  index 0 is source string
		//  index 1 is "///" || "/***"
		//  index 2 is "index-" index string
		//  index 3 is title

		if 4 == len(indexTitleMatch) {
			// extract title and z-index
			docStruct.SortTag = SFStringsUtil.ToInt(string(indexTitleMatch[2]))
			docStruct.Title = string(indexTitleMatch[3])
		}

		//  content parse
		contentBuf := bytes.NewBuffer(nil)
		var prefixTag []byte = nil
		prefixLen := 0

		for i := 1; i < linesCount-1; i++ {
			newLine := lines[i]

			if i == 1 {
				prefixTag = findPrefixFilterTag(newLine)
				prefixLen = len(prefixTag)
			}

			if nil != prefixTag {

				if 0 == bytes.Index(newLine, prefixTag) {
					contentBuf.Write(newLine[prefixLen:])
				} else {
					trimed := bytes.TrimSpace(newLine)
					// 有可能是空行，所需需要判断这行是否只有（ "*" || "//" ），如果不是则添加追加这一行内容
					if !bytes.Equal(trimed, _tagStar) && !bytes.Equal(trimed, _tagDSlash) {
						contentBuf.Write(newLine)
					}
				}

			} else {
				contentBuf.Write(newLine)
			}

			contentBuf.WriteByte('\n')
		}
		docStruct.Content = contentBuf.String()

		if 0 != len(docStruct.Content) {
			resultDocs = append(resultDocs, docStruct)
		}

	}

	return resultDocs
}

/**
 *  commons parse file about content
 *
 *  @param `fileBuf`
 *  @return about content
 */
func ParseAbout(fileBuf *FileBuf) *About {
	data := parseAboutAndIntro(fileBuf, REXAbout)

	var result *About = nil
	if 0 != len(data) {
		result = &About{Content: data}
	}

	return result
}

/**
 *  commons parse file introduction content
 *
 *  @param `fileBuf`
 *  @return introduction content
 */
func ParseIntro(fileBuf *FileBuf) *Intro {
	data := parseAboutAndIntro(fileBuf, REXIntro)

	var result *Intro = nil
	if 0 != len(data) {
		result = &Intro{Content: data}
	}

	return result
}

/**
 *  commons about intro parse
 *
 *  @param fileBuf
 *  @param rex
 */
func parseAboutAndIntro(fileBuf *FileBuf, rex *regexp.Regexp) []byte {
	var result []byte = nil
	var prefixTag []byte = nil
	prefixLen := 0

	buf := fileBuf.Find(rex)

	if 0 < len(buf) {
		appendLine := bytes.NewBuffer(nil)

		lines := bytes.Split(buf, []byte("\n"))
		linesCount := len(lines)

		for i := 1; i < linesCount-1; i++ {
			newLine := lines[i]

			if i == 1 {
				//  记录第一个前缀的标识，以第一个为准，后面的根据要求都要是相同的注释前缀。
				/**
				  (*)remove prefix tag
				  (*)
				  (*)
				*/
				prefixTag = findPrefixFilterTag(newLine)
				prefixLen = len(prefixTag)
			}

			if 0 != len(prefixTag) {

				if 0 == bytes.Index(newLine, prefixTag) {
					appendLine.Write(newLine[prefixLen:])
				} else {
					trimed := bytes.TrimSpace(newLine)
					// 有可能是空行，所需需要判断这行是否只有（ "*" || "//" ），如果不是则添加追加这一行内容
					if !bytes.Equal(trimed, _tagStar) && !bytes.Equal(trimed, _tagDSlash) {
						appendLine.Write(newLine)
					}
				}

			} else {
				appendLine.Write(newLine)
			}

			appendLine.WriteByte('\n')
		}

		if 0 < appendLine.Len() {
			result = appendLine.Bytes()
		}
	}

	return result
}

/**
 *  find prefix filter tag index
 *  //
 *  // content ("// ") is prefix tag
 *  //
 *  see var _prefixFilterTags
 */
func findPrefixFilterTag(src []byte) []byte {
	var pftCount = len(_prefixFilterTags)

	for i := 0; i < pftCount; i++ {
		prefix := _prefixFilterTags[i]
		if 0 == bytes.Index(src, prefix) {
			return prefix
		}
	}

	return nil
}
<commit_msg>修正：源文件的链接<commit_after>//  The MIT License (MIT) - http://opensource.org/licenses/MIT
//
//  Copyright (c) 2014 slowfei
//
//  Create on 2014-09-10
//  Update on 2014-11-05
//  Email  slowfei#foxmail.com
//  Home   http://www.slowfei.com

//
package gosfdoc

import (
	"bytes"
	"fmt"
	"github.com/slowfei/gosfcore/utils/strings"
	"os"
	"regexp"
	"strings"
)

const (
	DOC_FILE_SUFFIX = ".dc"      // document file suffix(document comments)
	NIL_DOC_NAME    = "document" // nilDocParser struct use

)

var (

	//  主要用于去除注释的前缀
	_prefixFilterTags = [][]byte{
		[]byte(" *\t"),
		[]byte(" *  "),
		[]byte(" * "),
		[]byte("//\t"),
		[]byte("//  "),
		[]byte("// "),
		[]byte("//"),
	}

	_tagStar   = []byte("*")  // comments (*)
	_tagDSlash = []byte("//") // double slash
)

/**
 *	nil document parser
 *	specifically for .doc file serve
 */
type nilDocParser struct {
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) Name() string {
	return NIL_DOC_NAME
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) CheckFile(path string, info os.FileInfo) bool {
	return strings.HasSuffix(path, DOC_FILE_SUFFIX)
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) EachIndexFile(filebuf *FileBuf) {

}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParsePreview(filebuf *FileBuf) []Preview {
	return nil
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParseCodeblock(filebuf *FileBuf) []CodeBlock {
	return nil
}

/**
 *	see DocParser interface
 */
func (n *nilDocParser) ParsePackageInfo(filebuf *FileBuf) string {
	return ""
}

/**
 *	prese Preview Document CodeBlock array to markdown
 *
 *	@param `documents` after sorting
 *	@param `previews`  after sorting
 *	@param `blocks`	   after sorting
 *	@param `filesName` file names
 *	@param `relPath`   before code file name join path
 *	@return bytes
 */
func ParseMarkdown(documents []Document, previews []Preview, blocks []CodeBlock,
	filesName []string, relPath string) []byte {

	relPath = strings.TrimPrefix(relPath, "/")
	relPath = strings.TrimSuffix(relPath, "/")
	joinSymbol := ""
	if 0 != len(relPath) {
		joinSymbol = "/"
	}
	isWrite := false
	buf := bytes.NewBuffer([]byte{'\n'})

	for _, doc := range documents {
		isWrite = true
		// ## Welcome to gosfdoc
		// ------
		//
		// markdown syntax content
		//
		buf.WriteString("## " + doc.Title + "\n------\n")
		buf.WriteString(doc.Content)
		buf.WriteByte('\n')
	}

	if 0 != len(previews) {
		isWrite = true
		// ## Preview
		// ------
		// > [func Main()][#]

		// > [type TestStruct struct][#]

		// > implements: [Test][#]

		// >>[func (* TestStruct) hello(str string) string](#func_TestStruct.hello)

		// >>[func (* TestStruct) hello2() string][#]

		buf.WriteString("## Preview\n------\n")
		for _, pre := range previews {
			buf.WriteByte('\n')
			angle := ">"
			for i := 0; i
"
			}

			anchor := ""
			if 0 == len(pre.Anchor) {
				anchor = "(javascript:;)"
				// [show text](javascript:;)
			} else {
				anchor = fmt.Sprintf("(#f_%s)", pre.Anchor, pre.Anchor)
				// [show test](#f_anchor)

			}
			buf.WriteString(angle + " [" + pre.ShowText + "]" + anchor + "\n")

			if 0 != len(pre.DescText) {
				buf.WriteByte('\n')
				buf.WriteString(angle + " " + pre.DescText + "\n")
			}
		}
		buf.WriteByte('\n')
	}

	// out associate files
	if 0 != len(filesName) {
		isWrite = true
		// ###Package files
		// [a.go](#) [b.go](#) [c.go](#)
		buf.WriteString("
\n### Directory files\n")
		for _, name := range filesName {
			joinPath := relPath + joinSymbol + name
			buf.WriteString(fmt.Sprintf("[%s](src.html?f=%s) ", name, joinPath))
		}
		buf.WriteByte('\n')
	}

	if 0 != len(blocks) {
		isWrite = true
		buf.WriteByte('\n')
		isLinkCode := 0 != len(filesName)
		// ## Func Details
		// ------
		// ###[func (*TestStruct) hello](src.html?f=gosfdoc.go#L17)  [↩](#preview_TestStruct.hello)|[#](#func_TestStruct.hello)
		// > 函数介绍描述

		// >

		// > @param `str` 字符串传递

		// > @return `v1` 返回参数v1

		// > @return v2 返回参数v2

		//
		// ```go
		// func (* TestStruct) hello(str string) (v1,v2 string)
		// ```
		currentMenuTitle := ""

		for _, block := range blocks {

			if 0 != len(block.MenuTitle) && currentMenuTitle != block.MenuTitle {
				buf.WriteString("## " + block.MenuTitle + "\n------\n")
				currentMenuTitle = block.MenuTitle
			}

			if 0 != len(block.Title) {
				joinPath := "javascript:;"
				if isLinkCode && 0 != len(block.SourceFileName) {
					lineStr := ""

					lineLen := len(block.FileLines)
					if 1 == lineLen {
						lineStr = fmt.Sprintf("#L%d", block.FileLines[0])
					} else if 2 == lineLen {
						lineStr = fmt.Sprintf("#L%d-L%d", block.FileLines[0], block.FileLines[1])
					}

					joinPath = "src.html?f=" + relPath + joinSymbol + block.SourceFileName + lineStr
				}

				anchor := ""
				if 0 != len(block.Anchor) {
					anchor = fmt.Sprintf(" [↩](#p_%s) | [#](#f_%s)", block.Anchor, block.Anchor, block.Anchor)
				}

				buf.WriteString(fmt.Sprintf("### [%s](%s) %s\n", block.Title, joinPath, anchor))
			}

			if 0 != len(block.Desc) {
				//	content description
				descLines := strings.Split(block.Desc, "\n")
				for _, desc := range descLines {
					buf.WriteString(fmt.Sprintf("> %s
\n", desc))
				}
				buf.WriteByte('\n')
			}

			// code
			if 0 != len(block.Code) {
				buf.WriteString(fmt.Sprintf("\n```%s\n%s\n```\n\n", block.CodeLang, block.Code))
			}

			buf.WriteByte('\n')
		}
	}

	if isWrite {
		return buf.Bytes()
	} else {
		return nil
	}
}

/**
 *  parse public document content
 *
 *  @param `fileBuf`
 *  @return document array
 */
func ParseDocument(fileBuf *FileBuf) []Document {
	var resultDocs []Document = nil

	docsBuf := fileBuf.FinaAll(REXDocument)
	docsCount := len(docsBuf)

	if 0 == docsCount {
		return resultDocs
	}

	resultDocs = make([]Document, 0, docsCount)

	for i := 0; i < docsCount; i++ {
		docStruct := Document{}
		buf := docsBuf[i]

		lines := bytes.Split(buf, []byte("\n"))
		linesCount := len(lines)

		//  title and index parse
		indexTitleLine := lines[0]
		indexTitleMatch := REXDocIndexTitle.FindSubmatch(indexTitleLine)
		//  index 0 is source string
		//  index 1 is "///" || "/***"
		//  index 2 is "index-" index string
		//  index 3 is title

		if 4 == len(indexTitleMatch) {
			// extract title and z-index
			docStruct.SortTag = SFStringsUtil.ToInt(string(indexTitleMatch[2]))
			docStruct.Title = string(indexTitleMatch[3])
		}

		//  content parse
		contentBuf := bytes.NewBuffer(nil)
		var prefixTag []byte = nil
		prefixLen := 0

		for i := 1; i < linesCount-1; i++ {
			newLine := lines[i]

			if i == 1 {
				prefixTag = findPrefixFilterTag(newLine)
				prefixLen = len(prefixTag)
			}

			if nil != prefixTag {

				if 0 == bytes.Index(newLine, prefixTag) {
					contentBuf.Write(newLine[prefixLen:])
				} else {
					trimed := bytes.TrimSpace(newLine)
					// 有可能是空行，所需需要判断这行是否只有（ "*" || "//" ），如果不是则添加追加这一行内容
					if !bytes.Equal(trimed, _tagStar) && !bytes.Equal(trimed, _tagDSlash) {
						contentBuf.Write(newLine)
					}
				}

			} else {
				contentBuf.Write(newLine)
			}

			contentBuf.WriteByte('\n')
		}
		docStruct.Content = contentBuf.String()

		if 0 != len(docStruct.Content) {
			resultDocs = append(resultDocs, docStruct)
		}

	}

	return resultDocs
}

/**
 *  commons parse file about content
 *
 *  @param `fileBuf`
 *  @return about content
 */
func ParseAbout(fileBuf *FileBuf) *About {
	data := parseAboutAndIntro(fileBuf, REXAbout)

	var result *About = nil
	if 0 != len(data) {
		result = &About{Content: data}
	}

	return result
}

/**
 *  commons parse file introduction content
 *
 *  @param `fileBuf`
 *  @return introduction content
 */
func ParseIntro(fileBuf *FileBuf) *Intro {
	data := parseAboutAndIntro(fileBuf, REXIntro)

	var result *Intro = nil
	if 0 != len(data) {
		result = &Intro{Content: data}
	}

	return result
}

/**
 *  commons about intro parse
 *
 *  @param fileBuf
 *  @param rex
 */
func parseAboutAndIntro(fileBuf *FileBuf, rex *regexp.Regexp) []byte {
	var result []byte = nil
	var prefixTag []byte = nil
	prefixLen := 0

	buf := fileBuf.Find(rex)

	if 0 < len(buf) {
		appendLine := bytes.NewBuffer(nil)

		lines := bytes.Split(buf, []byte("\n"))
		linesCount := len(lines)

		for i := 1; i < linesCount-1; i++ {
			newLine := lines[i]

			if i == 1 {
				//  记录第一个前缀的标识，以第一个为准，后面的根据要求都要是相同的注释前缀。
				/**
				  (*)remove prefix tag
				  (*)
				  (*)
				*/
				prefixTag = findPrefixFilterTag(newLine)
				prefixLen = len(prefixTag)
			}

			if 0 != len(prefixTag) {

				if 0 == bytes.Index(newLine, prefixTag) {
					appendLine.Write(newLine[prefixLen:])
				} else {
					trimed := bytes.TrimSpace(newLine)
					// 有可能是空行，所需需要判断这行是否只有（ "*" || "//" ），如果不是则添加追加这一行内容
					if !bytes.Equal(trimed, _tagStar) && !bytes.Equal(trimed, _tagDSlash) {
						appendLine.Write(newLine)
					}
				}

			} else {
				appendLine.Write(newLine)
			}

			appendLine.WriteByte('\n')
		}

		if 0 < appendLine.Len() {
			result = appendLine.Bytes()
		}
	}

	return result
}

/**
 *  find prefix filter tag index
 *  //
 *  // content ("// ") is prefix tag
 *  //
 *  see var _prefixFilterTags
 */
func findPrefixFilterTag(src []byte) []byte {
	var pftCount = len(_prefixFilterTags)

	for i := 0; i < pftCount; i++ {
		prefix := _prefixFilterTags[i]
		if 0 == bytes.Index(src, prefix) {
			return prefix
		}
	}

	return nil
}
<|endoftext|>