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

// Package redirect provides hooks to register HTTP handlers that redirect old
// godoc paths to their new equivalents and assist in accessing the issue
// tracker, wiki, code review system, etc.
package redirect

import (
	"net/http"
	"regexp"
)

// Register registers HTTP handlers that redirect old godoc paths to their new
// equivalents and assist in accessing the issue tracker, wiki, code review
// system, etc. If mux is nil it uses http.DefaultServeMux.
func Register(mux *http.ServeMux) {
	if mux == nil {
		mux = http.DefaultServeMux
	}
	handlePathRedirects(mux, pkgRedirects, "/pkg/")
	handlePathRedirects(mux, cmdRedirects, "/cmd/")
	for prefix, redirect := range prefixHelpers {
		p := "/" + prefix + "/"
		mux.Handle(p, PrefixHandler(p, redirect))
	}
	for path, redirect := range redirects {
		mux.Handle(path, Handler(redirect))
	}
}

func handlePathRedirects(mux *http.ServeMux, redirects map[string]string, prefix string) {
	for source, target := range redirects {
		h := Handler(prefix + target + "/")
		p := prefix + source
		mux.Handle(p, h)
		mux.Handle(p+"/", h)
	}
}

// Packages that were renamed between r60 and go1.
var pkgRedirects = map[string]string{
	"asn1":              "encoding/asn1",
	"big":               "math/big",
	"cmath":             "math/cmplx",
	"csv":               "encoding/csv",
	"exec":              "os/exec",
	"exp/template/html": "html/template",
	"gob":               "encoding/gob",
	"http":              "net/http",
	"http/cgi":          "net/http/cgi",
	"http/fcgi":         "net/http/fcgi",
	"http/httptest":     "net/http/httptest",
	"http/pprof":        "net/http/pprof",
	"json":              "encoding/json",
	"mail":              "net/mail",
	"rand":              "math/rand",
	"rpc":               "net/rpc",
	"rpc/jsonrpc":       "net/rpc/jsonrpc",
	"scanner":           "text/scanner",
	"smtp":              "net/smtp",
	"tabwriter":         "text/tabwriter",
	"template":          "text/template",
	"template/parse":    "text/template/parse",
	"url":               "net/url",
	"utf16":             "unicode/utf16",
	"utf8":              "unicode/utf8",
	"xml":               "encoding/xml",
}

// Commands that were renamed between r60 and go1.
var cmdRedirects = map[string]string{
	"gofix":     "fix",
	"goinstall": "go",
	"gopack":    "pack",
	"gotest":    "go",
	"govet":     "vet",
	"goyacc":    "yacc",
}

var redirects = map[string]string{
	"/blog":       "/blog/",
	"/build":      "http://build.golang.org",
	"/change":     "https://code.google.com/p/go/source/list",
	"/cl":         "https://gocodereview.appspot.com/",
	"/cmd/godoc/": "http://godoc.org/code.google.com/p/go.tools/cmd/godoc/",
	"/cmd/vet/":   "http://godoc.org/code.google.com/p/go.tools/cmd/vet/",
	"/issue":      "https://code.google.com/p/go/issues",
	"/issue/new":  "https://code.google.com/p/go/issues/entry",
	"/issues":     "https://code.google.com/p/go/issues",
	"/play":       "http://play.golang.org",

	// THESE DO NOT WORK FOR GO 1.1 SITES SUCH AS golang.org.
	// DO NOT RE-ENABLE.
	// "/ref":        "/doc/#references",
	// "/ref/":       "/doc/#references",
	// "/ref/mem":    "/doc/mem",
	// "/ref/spec":   "/doc/spec",

	// In fact, becuase golang.org pulls some pages from tip, there
	// are already links on the main golang.org page pointing at
	// the non-existent /doc/spec etc URLs. So redirect the other way.
	"/doc/mem":  "/ref/mem",
	"/doc/spec": "/ref/spec",

	"/talks": "http://talks.golang.org",
	"/tour":  "http://tour.golang.org",
	"/wiki":  "https://code.google.com/p/go-wiki/w/list",

	"/doc/articles/c_go_cgo.html":                    "/blog/c-go-cgo",
	"/doc/articles/concurrency_patterns.html":        "/blog/go-concurrency-patterns-timing-out-and",
	"/doc/articles/defer_panic_recover.html":         "/blog/defer-panic-and-recover",
	"/doc/articles/error_handling.html":              "/blog/error-handling-and-go",
	"/doc/articles/gobs_of_data.html":                "/blog/gobs-of-data",
	"/doc/articles/godoc_documenting_go_code.html":   "/blog/godoc-documenting-go-code",
	"/doc/articles/gos_declaration_syntax.html":      "/blog/gos-declaration-syntax",
	"/doc/articles/image_draw.html":                  "/blog/go-imagedraw-package",
	"/doc/articles/image_package.html":               "/blog/go-image-package",
	"/doc/articles/json_and_go.html":                 "/blog/json-and-go",
	"/doc/articles/json_rpc_tale_of_interfaces.html": "/blog/json-rpc-tale-of-interfaces",
	"/doc/articles/laws_of_reflection.html":          "/blog/laws-of-reflection",
	"/doc/articles/race_detector.html":               "/blog/race-detector",
	"/doc/articles/slices_usage_and_internals.html":  "/blog/go-slices-usage-and-internals",
	"/doc/go_for_cpp_programmers.html":               "https://code.google.com/p/go-wiki/wiki/GoForCPPProgrammers",
	"/doc/go_tutorial.html":                          "http://tour.golang.org/",
}

var prefixHelpers = map[string]string{
	"change": "https://code.google.com/p/go/source/detail?r=",
	"cl":     "https://codereview.appspot.com/",
	"issue":  "https://code.google.com/p/go/issues/detail?id=",
	"play":   "http://play.golang.org/",
	"talks":  "http://talks.golang.org/",
	"wiki":   "https://code.google.com/p/go-wiki/wiki/",
}

func Handler(target string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, target, http.StatusMovedPermanently)
	})
}

var validId = regexp.MustCompile(`^[A-Za-z0-9-]*$`)

func PrefixHandler(prefix, baseURL string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if p := r.URL.Path; p == prefix {
			// redirect /prefix/ to /prefix
			http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
			return
		}
		id := r.URL.Path[len(prefix):]
		if !validId.MatchString(id) {
			http.Error(w, "Not found", http.StatusNotFound)
			return
		}
		target := baseURL + id
		http.Redirect(w, r, target, http.StatusFound)
	})
}
<commit_msg>go.tools/godoc/redirect: remove redundant redirect rules<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package redirect provides hooks to register HTTP handlers that redirect old
// godoc paths to their new equivalents and assist in accessing the issue
// tracker, wiki, code review system, etc.
package redirect

import (
	"net/http"
	"regexp"
)

// Register registers HTTP handlers that redirect old godoc paths to their new
// equivalents and assist in accessing the issue tracker, wiki, code review
// system, etc. If mux is nil it uses http.DefaultServeMux.
func Register(mux *http.ServeMux) {
	if mux == nil {
		mux = http.DefaultServeMux
	}
	handlePathRedirects(mux, pkgRedirects, "/pkg/")
	handlePathRedirects(mux, cmdRedirects, "/cmd/")
	for prefix, redirect := range prefixHelpers {
		p := "/" + prefix + "/"
		mux.Handle(p, PrefixHandler(p, redirect))
	}
	for path, redirect := range redirects {
		mux.Handle(path, Handler(redirect))
	}
}

func handlePathRedirects(mux *http.ServeMux, redirects map[string]string, prefix string) {
	for source, target := range redirects {
		h := Handler(prefix + target + "/")
		p := prefix + source
		mux.Handle(p, h)
		mux.Handle(p+"/", h)
	}
}

// Packages that were renamed between r60 and go1.
var pkgRedirects = map[string]string{
	"asn1":              "encoding/asn1",
	"big":               "math/big",
	"cmath":             "math/cmplx",
	"csv":               "encoding/csv",
	"exec":              "os/exec",
	"exp/template/html": "html/template",
	"gob":               "encoding/gob",
	"http":              "net/http",
	"http/cgi":          "net/http/cgi",
	"http/fcgi":         "net/http/fcgi",
	"http/httptest":     "net/http/httptest",
	"http/pprof":        "net/http/pprof",
	"json":              "encoding/json",
	"mail":              "net/mail",
	"rand":              "math/rand",
	"rpc":               "net/rpc",
	"rpc/jsonrpc":       "net/rpc/jsonrpc",
	"scanner":           "text/scanner",
	"smtp":              "net/smtp",
	"tabwriter":         "text/tabwriter",
	"template":          "text/template",
	"template/parse":    "text/template/parse",
	"url":               "net/url",
	"utf16":             "unicode/utf16",
	"utf8":              "unicode/utf8",
	"xml":               "encoding/xml",
}

// Commands that were renamed between r60 and go1.
var cmdRedirects = map[string]string{
	"gofix":     "fix",
	"goinstall": "go",
	"gopack":    "pack",
	"gotest":    "go",
	"govet":     "vet",
	"goyacc":    "yacc",
}

var redirects = map[string]string{
	"/blog":       "/blog/",
	"/build":      "http://build.golang.org",
	"/change":     "https://code.google.com/p/go/source/list",
	"/cl":         "https://gocodereview.appspot.com/",
	"/cmd/godoc/": "http://godoc.org/code.google.com/p/go.tools/cmd/godoc/",
	"/cmd/vet/":   "http://godoc.org/code.google.com/p/go.tools/cmd/vet/",
	"/issue":      "https://code.google.com/p/go/issues",
	"/issue/new":  "https://code.google.com/p/go/issues/entry",
	"/issues":     "https://code.google.com/p/go/issues",
	"/play":       "http://play.golang.org",

	// In Go 1.2 the references page is part of /doc/.
	"/ref": "/doc/#references",
	// This next rule clobbers /ref/spec and /ref/mem.
	// TODO(adg): figure out what to do here, if anything.
	// "/ref/": "/doc/#references",

	// Be nice to people who are looking in the wrong place.
	"/doc/mem":  "/ref/mem",
	"/doc/spec": "/ref/spec",

	"/talks": "http://talks.golang.org",
	"/tour":  "http://tour.golang.org",
	"/wiki":  "https://code.google.com/p/go-wiki/w/list",

	"/doc/articles/c_go_cgo.html":                    "/blog/c-go-cgo",
	"/doc/articles/concurrency_patterns.html":        "/blog/go-concurrency-patterns-timing-out-and",
	"/doc/articles/defer_panic_recover.html":         "/blog/defer-panic-and-recover",
	"/doc/articles/error_handling.html":              "/blog/error-handling-and-go",
	"/doc/articles/gobs_of_data.html":                "/blog/gobs-of-data",
	"/doc/articles/godoc_documenting_go_code.html":   "/blog/godoc-documenting-go-code",
	"/doc/articles/gos_declaration_syntax.html":      "/blog/gos-declaration-syntax",
	"/doc/articles/image_draw.html":                  "/blog/go-imagedraw-package",
	"/doc/articles/image_package.html":               "/blog/go-image-package",
	"/doc/articles/json_and_go.html":                 "/blog/json-and-go",
	"/doc/articles/json_rpc_tale_of_interfaces.html": "/blog/json-rpc-tale-of-interfaces",
	"/doc/articles/laws_of_reflection.html":          "/blog/laws-of-reflection",
	"/doc/articles/race_detector.html":               "/blog/race-detector",
	"/doc/articles/slices_usage_and_internals.html":  "/blog/go-slices-usage-and-internals",
	"/doc/go_for_cpp_programmers.html":               "https://code.google.com/p/go-wiki/wiki/GoForCPPProgrammers",
	"/doc/go_tutorial.html":                          "http://tour.golang.org/",
}

var prefixHelpers = map[string]string{
	"change": "https://code.google.com/p/go/source/detail?r=",
	"cl":     "https://codereview.appspot.com/",
	"issue":  "https://code.google.com/p/go/issues/detail?id=",
	"play":   "http://play.golang.org/",
	"talks":  "http://talks.golang.org/",
	"wiki":   "https://code.google.com/p/go-wiki/wiki/",
}

func Handler(target string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, target, http.StatusMovedPermanently)
	})
}

var validId = regexp.MustCompile(`^[A-Za-z0-9-]*$`)

func PrefixHandler(prefix, baseURL string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if p := r.URL.Path; p == prefix {
			// redirect /prefix/ to /prefix
			http.Redirect(w, r, p[:len(p)-1], http.StatusFound)
			return
		}
		id := r.URL.Path[len(prefix):]
		if !validId.MatchString(id) {
			http.Error(w, "Not found", http.StatusNotFound)
			return
		}
		target := baseURL + id
		http.Redirect(w, r, target, http.StatusFound)
	})
}
<|endoftext|>


<commit_before>// Configurations manipulations, all configurations (including user configurations) are stored in wide.json.
package conf

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"
	"time"

	"github.com/b3log/wide/event"
	"github.com/b3log/wide/util"
	"github.com/golang/glog"
)

const (
	PathSeparator     = string(os.PathSeparator)     // OS-specific path separator
	PathListSeparator = string(os.PathListSeparator) // OS-specific path list separator
)

const (
	WideVersion   = "1.0.1" // wide version
	CodeMirrorVer = "4.7"   // editor version
)

// The latest session content.
type LatestSessionContent struct {
	FileTree    []string // paths of expanding nodes of file tree
	Files       []string // paths of files of opening editor tabs
	CurrentFile string   // path of file of the current focused editor tab
}

// User configuration.
type User struct {
	Name                 string
	Password             string
	Workspace            string // the GOPATH of this user
	Locale               string
	GoFormat             string
	FontFamily           string
	FontSize             string
	Editor               *Editor
	LatestSessionContent *LatestSessionContent
}

// Editor configuration of a user.
type Editor struct {
	FontFamily string
	FontSize   string
}

// Configuration.
type conf struct {
	IP                    string  // server ip, ${ip}
	Server                string  // server host and port ({IP}:7070)
	StaticServer          string  // static resources server scheme, host and port (http://{IP}:7070)
	EditorChannel         string  // editor channel (ws://{IP}:7070)
	OutputChannel         string  // output channel (ws://{IP}:7070)
	ShellChannel          string  // shell channel(ws://{IP}:7070)
	SessionChannel        string  // wide session channel (ws://{IP}:7070)
	HTTPSessionMaxAge     int     // HTTP session max age (in seciond)
	StaticResourceVersion string  // version of static resources
	MaxProcs              int     // Go max procs
	RuntimeMode           string  // runtime mode (dev/prod)
	WD                    string  // current working direcitory, ${pwd}
	Workspace             string  // path of master workspace
	Locale                string  // default locale
	Users                 []*User // configurations of users
}

// Configuration variable.
var Wide conf

// A raw copy of configuration variable.
//
// Save function will use this variable to persist.
var rawWide conf

// FixedTimeCheckEnv checks Wide runtime enviorment periodically (7 minutes).
//
// Exits process if found fatal issues (such as not found $GOPATH),
// Notifies user by notification queue if found warning issues (such as not found gocode).
func FixedTimeCheckEnv() {
	checkEnv() // check immediately

	go func() {
		for _ = range time.Tick(time.Minute * 7) {
			checkEnv()
		}
	}()
}

func checkEnv() {
	if "" == os.Getenv("GOPATH") {
		glog.Fatal("Not found $GOPATH, please configure it before running Wide")

		os.Exit(-1)
	}

	if "" == os.Getenv("GOROOT") {
		glog.Fatal("Not found $GOROOT, please configure it before running Wide")

		os.Exit(-1)
	}

	gocode := Wide.GetExecutableInGOBIN("gocode")
	cmd := exec.Command(gocode, "close")
	_, err := cmd.Output()
	if nil != err {
		event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}

		glog.Warningf("Not found gocode [%s]", gocode)
	}

	ide_stub := Wide.GetExecutableInGOBIN("ide_stub")
	cmd = exec.Command(ide_stub, "version")
	_, err = cmd.Output()
	if nil != err {
		event.EventQueue <- &event.Event{Code: event.EvtCodeIDEStubNotFound}

		glog.Warningf("Not found ide_stub [%s]", ide_stub)
	}
}

// FixedTimeSave saves configurations (wide.json) periodically (1 minute).
//
// Main goal of this function is to save user session content, for restoring session content while user open Wide next time.
func FixedTimeSave() {
	go func() {
		for _ = range time.Tick(time.Minute) {
			Save()
		}
	}()
}

// GetUserWorkspace gets workspace path with the specified username, returns "" if not found.
func (c *conf) GetUserWorkspace(username string) string {
	for _, user := range c.Users {
		if user.Name == username {
			ret := strings.Replace(user.Workspace, "{WD}", c.WD, 1)

			return filepath.FromSlash(ret)
		}
	}

	return ""
}

// GetWorkspace gets the master workspace path.
//
// Compared to the use of Wide.Workspace, this function will be processed as follows:
//  1. Replace {WD} variable with the actual directory path
//  2. Replace "/" with "\\" (Windows)
func (c *conf) GetWorkspace() string {
	return filepath.FromSlash(strings.Replace(c.Workspace, "{WD}", c.WD, 1))
}

// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found.
func (c *conf) GetGoFmt(username string) string {
	for _, user := range c.Users {
		if user.Name == username {
			switch user.GoFormat {
			case "gofmt":
				return "gofmt"
			case "goimports":
				return c.GetExecutableInGOBIN("goimports")
			default:
				glog.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
				return "gofmt"
			}
		}
	}

	return "gofmt"
}

// GetWorkspace gets workspace path of the user.
//
// Compared to the use of Wide.Workspace, this function will be processed as follows:
//  1. Replace {WD} variable with the actual directory path
//  2. Replace "/" with "\\" (Windows)
func (u *User) GetWorkspace() string {
	return filepath.FromSlash(strings.Replace(u.Workspace, "{WD}", Wide.WD, 1))
}

// GetUser gets configuration of the user specified by the given username, returns nil if not found.
func (*conf) GetUser(username string) *User {
	for _, user := range Wide.Users {
		if user.Name == username {
			return user
		}
	}

	return nil
}

// GetExecutableInGOBIN gets executable file under GOBIN path.
//
// The specified executable should not with extension, this function will append .exe if on Windows.
func (*conf) GetExecutableInGOBIN(executable string) string {
	if util.OS.IsWindows() {
		executable += ".exe"
	}

	gopaths := filepath.SplitList(os.Getenv("GOPATH"))

	for _, gopath := range gopaths {
		// $GOPATH/bin/$GOOS_$GOARCH/executable
		ret := gopath + PathSeparator + "bin" + PathSeparator +
			os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") + PathSeparator + executable
		if isExist(ret) {
			return ret
		}

		// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/executable
		ret = gopath + PathSeparator + "bin" + PathSeparator +
			runtime.GOOS + "_" + runtime.GOARCH + PathSeparator + executable
		if isExist(ret) {
			return ret
		}

		// $GOPATH/bin/executable
		ret = gopath + PathSeparator + "bin" + PathSeparator + executable
		if isExist(ret) {
			return ret
		}
	}

	// $GOBIN/executable
	return os.Getenv("GOBIN") + PathSeparator + executable
}

// Save saves Wide configurations.
func Save() bool {
	// just the Users field are volatile
	rawWide.Users = Wide.Users

	// format
	bytes, err := json.MarshalIndent(rawWide, "", "    ")

	if nil != err {
		glog.Error(err)

		return false
	}

	if err = ioutil.WriteFile("conf/wide.json", bytes, 0644); nil != err {
		glog.Error(err)

		return false
	}

	return true
}

// Load loads the configurations from wide.json.
func Load() {
	bytes, _ := ioutil.ReadFile("conf/wide.json")

	err := json.Unmarshal(bytes, &Wide)
	if err != nil {
		glog.Error(err)

		os.Exit(-1)
	}

	// keep the raw content
	json.Unmarshal(bytes, &rawWide)

	ip, err := util.Net.LocalIP()
	if err != nil {
		glog.Error(err)

		os.Exit(-1)
	}

	glog.V(5).Infof("${ip} [%s]", ip)

	Wide.WD = util.OS.Pwd()
	glog.V(5).Infof("${pwd} [%s]", Wide.WD)

	Wide.IP = strings.Replace(Wide.IP, "${ip}", ip, 1)

	Wide.Server = strings.Replace(Wide.Server, "{IP}", Wide.IP, 1)
	Wide.StaticServer = strings.Replace(Wide.StaticServer, "{IP}", Wide.IP, 1)
	Wide.EditorChannel = strings.Replace(Wide.EditorChannel, "{IP}", Wide.IP, 1)
	Wide.OutputChannel = strings.Replace(Wide.OutputChannel, "{IP}", Wide.IP, 1)
	Wide.ShellChannel = strings.Replace(Wide.ShellChannel, "{IP}", Wide.IP, 1)
	Wide.SessionChannel = strings.Replace(Wide.SessionChannel, "{IP}", Wide.IP, 1)

	glog.V(5).Info("Conf: \n" + string(bytes))

	initWorkspaceDirs()
	initCustomizedConfs()
}

// initCustomizedConfs initializes the user customized configurations.
func initCustomizedConfs() {
	for _, user := range Wide.Users {
		UpdateCustomizedConf(user.Name)
	}
}

// UpdateCustomizedConf creates (if not exists) or updates user customized configuration files.
//
//  1. /static/user/{username}/style.css
func UpdateCustomizedConf(username string) {
	var u *User = nil
	for _, user := range Wide.Users { // maybe it is a beauty of the trade-off of the another world between design and implementation
		if user.Name == username {
			u = user
		}
	}

	if nil == u {
		return
	}

	model := map[string]interface{}{"user": u}

	t, err := template.ParseFiles("static/user/style.css.tmpl")
	if nil != err {
		glog.Error(err)

		os.Exit(-1)
	}

	wd := util.OS.Pwd()
	dir := filepath.Clean(wd + "/static/user/" + u.Name)
	if err := os.MkdirAll(dir, 0664); nil != err {
		glog.Error(err)

		os.Exit(-1)
	}

	fout, err := os.Create(dir + PathSeparator + "style.css")
	if nil != err {
		glog.Error(err)

		os.Exit(-1)
	}

	defer fout.Close()

	if err := t.Execute(fout, model); nil != err {
		glog.Error(err)

		os.Exit(-1)
	}
}

// initWorkspaceDirs initializes the directories of master workspace, users' workspaces.
//
// Creates directories if not found on path of workspace.
func initWorkspaceDirs() {
	paths := filepath.SplitList(Wide.GetWorkspace())

	for _, user := range Wide.Users {
		paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
	}

	for _, path := range paths {
		CreateWorkspaceDir(path)
	}
}

// CreateWorkspaceDir creates (if not exists) directories on the path.
//
//  1. root directory:{path}
//  2. src directory: {path}/src
//  3. package directory: {path}/pkg
//  4. binary directory: {path}/bin
func CreateWorkspaceDir(path string) {
	createDir(path)
	createDir(path + PathSeparator + "src")
	createDir(path + PathSeparator + "pkg")
	createDir(path + PathSeparator + "bin")
}

// createDir creates a directory on the path if it not exists.
func createDir(path string) {
	if !isExist(path) {
		if err := os.MkdirAll(path, 0775); nil != err {
			glog.Error(err)

			os.Exit(-1)
		}

		glog.V(7).Infof("Created a directory [%s]", path)
	}
}

// isExist determines whether the file spcified by the given filename is exists.
func isExist(filename string) bool {
	_, err := os.Stat(filename)

	return err == nil || os.IsExist(err)
}
<commit_msg>fix create user workspace failed in linux<commit_after>// Configurations manipulations, all configurations (including user configurations) are stored in wide.json.
package conf

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"
	"time"

	"github.com/b3log/wide/event"
	"github.com/b3log/wide/util"
	"github.com/golang/glog"
)

const (
	PathSeparator     = string(os.PathSeparator)     // OS-specific path separator
	PathListSeparator = string(os.PathListSeparator) // OS-specific path list separator
)

const (
	WideVersion   = "1.0.1" // wide version
	CodeMirrorVer = "4.7"   // editor version
)

// The latest session content.
type LatestSessionContent struct {
	FileTree    []string // paths of expanding nodes of file tree
	Files       []string // paths of files of opening editor tabs
	CurrentFile string   // path of file of the current focused editor tab
}

// User configuration.
type User struct {
	Name                 string
	Password             string
	Workspace            string // the GOPATH of this user
	Locale               string
	GoFormat             string
	FontFamily           string
	FontSize             string
	Editor               *Editor
	LatestSessionContent *LatestSessionContent
}

// Editor configuration of a user.
type Editor struct {
	FontFamily string
	FontSize   string
}

// Configuration.
type conf struct {
	IP                    string  // server ip, ${ip}
	Server                string  // server host and port ({IP}:7070)
	StaticServer          string  // static resources server scheme, host and port (http://{IP}:7070)
	EditorChannel         string  // editor channel (ws://{IP}:7070)
	OutputChannel         string  // output channel (ws://{IP}:7070)
	ShellChannel          string  // shell channel(ws://{IP}:7070)
	SessionChannel        string  // wide session channel (ws://{IP}:7070)
	HTTPSessionMaxAge     int     // HTTP session max age (in seciond)
	StaticResourceVersion string  // version of static resources
	MaxProcs              int     // Go max procs
	RuntimeMode           string  // runtime mode (dev/prod)
	WD                    string  // current working direcitory, ${pwd}
	Workspace             string  // path of master workspace
	Locale                string  // default locale
	Users                 []*User // configurations of users
}

// Configuration variable.
var Wide conf

// A raw copy of configuration variable.
//
// Save function will use this variable to persist.
var rawWide conf

// FixedTimeCheckEnv checks Wide runtime enviorment periodically (7 minutes).
//
// Exits process if found fatal issues (such as not found $GOPATH),
// Notifies user by notification queue if found warning issues (such as not found gocode).
func FixedTimeCheckEnv() {
	checkEnv() // check immediately

	go func() {
		for _ = range time.Tick(time.Minute * 7) {
			checkEnv()
		}
	}()
}

func checkEnv() {
	if "" == os.Getenv("GOPATH") {
		glog.Fatal("Not found $GOPATH, please configure it before running Wide")

		os.Exit(-1)
	}

	if "" == os.Getenv("GOROOT") {
		glog.Fatal("Not found $GOROOT, please configure it before running Wide")

		os.Exit(-1)
	}

	gocode := Wide.GetExecutableInGOBIN("gocode")
	cmd := exec.Command(gocode, "close")
	_, err := cmd.Output()
	if nil != err {
		event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}

		glog.Warningf("Not found gocode [%s]", gocode)
	}

	ide_stub := Wide.GetExecutableInGOBIN("ide_stub")
	cmd = exec.Command(ide_stub, "version")
	_, err = cmd.Output()
	if nil != err {
		event.EventQueue <- &event.Event{Code: event.EvtCodeIDEStubNotFound}

		glog.Warningf("Not found ide_stub [%s]", ide_stub)
	}
}

// FixedTimeSave saves configurations (wide.json) periodically (1 minute).
//
// Main goal of this function is to save user session content, for restoring session content while user open Wide next time.
func FixedTimeSave() {
	go func() {
		for _ = range time.Tick(time.Minute) {
			Save()
		}
	}()
}

// GetUserWorkspace gets workspace path with the specified username, returns "" if not found.
func (c *conf) GetUserWorkspace(username string) string {
	for _, user := range c.Users {
		if user.Name == username {
			ret := strings.Replace(user.Workspace, "{WD}", c.WD, 1)

			return filepath.FromSlash(ret)
		}
	}

	return ""
}

// GetWorkspace gets the master workspace path.
//
// Compared to the use of Wide.Workspace, this function will be processed as follows:
//  1. Replace {WD} variable with the actual directory path
//  2. Replace "/" with "\\" (Windows)
func (c *conf) GetWorkspace() string {
	return filepath.FromSlash(strings.Replace(c.Workspace, "{WD}", c.WD, 1))
}

// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found.
func (c *conf) GetGoFmt(username string) string {
	for _, user := range c.Users {
		if user.Name == username {
			switch user.GoFormat {
			case "gofmt":
				return "gofmt"
			case "goimports":
				return c.GetExecutableInGOBIN("goimports")
			default:
				glog.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
				return "gofmt"
			}
		}
	}

	return "gofmt"
}

// GetWorkspace gets workspace path of the user.
//
// Compared to the use of Wide.Workspace, this function will be processed as follows:
//  1. Replace {WD} variable with the actual directory path
//  2. Replace "/" with "\\" (Windows)
func (u *User) GetWorkspace() string {
	return filepath.FromSlash(strings.Replace(u.Workspace, "{WD}", Wide.WD, 1))
}

// GetUser gets configuration of the user specified by the given username, returns nil if not found.
func (*conf) GetUser(username string) *User {
	for _, user := range Wide.Users {
		if user.Name == username {
			return user
		}
	}

	return nil
}

// GetExecutableInGOBIN gets executable file under GOBIN path.
//
// The specified executable should not with extension, this function will append .exe if on Windows.
func (*conf) GetExecutableInGOBIN(executable string) string {
	if util.OS.IsWindows() {
		executable += ".exe"
	}

	gopaths := filepath.SplitList(os.Getenv("GOPATH"))

	for _, gopath := range gopaths {
		// $GOPATH/bin/$GOOS_$GOARCH/executable
		ret := gopath + PathSeparator + "bin" + PathSeparator +
			os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") + PathSeparator + executable
		if isExist(ret) {
			return ret
		}

		// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/executable
		ret = gopath + PathSeparator + "bin" + PathSeparator +
			runtime.GOOS + "_" + runtime.GOARCH + PathSeparator + executable
		if isExist(ret) {
			return ret
		}

		// $GOPATH/bin/executable
		ret = gopath + PathSeparator + "bin" + PathSeparator + executable
		if isExist(ret) {
			return ret
		}
	}

	// $GOBIN/executable
	return os.Getenv("GOBIN") + PathSeparator + executable
}

// Save saves Wide configurations.
func Save() bool {
	// just the Users field are volatile
	rawWide.Users = Wide.Users

	// format
	bytes, err := json.MarshalIndent(rawWide, "", "    ")

	if nil != err {
		glog.Error(err)

		return false
	}

	if err = ioutil.WriteFile("conf/wide.json", bytes, 0644); nil != err {
		glog.Error(err)

		return false
	}

	return true
}

// Load loads the configurations from wide.json.
func Load() {
	bytes, _ := ioutil.ReadFile("conf/wide.json")

	err := json.Unmarshal(bytes, &Wide)
	if err != nil {
		glog.Error(err)

		os.Exit(-1)
	}

	// keep the raw content
	json.Unmarshal(bytes, &rawWide)

	ip, err := util.Net.LocalIP()
	if err != nil {
		glog.Error(err)

		os.Exit(-1)
	}

	glog.V(5).Infof("${ip} [%s]", ip)

	Wide.WD = util.OS.Pwd()
	glog.V(5).Infof("${pwd} [%s]", Wide.WD)

	Wide.IP = strings.Replace(Wide.IP, "${ip}", ip, 1)

	Wide.Server = strings.Replace(Wide.Server, "{IP}", Wide.IP, 1)
	Wide.StaticServer = strings.Replace(Wide.StaticServer, "{IP}", Wide.IP, 1)
	Wide.EditorChannel = strings.Replace(Wide.EditorChannel, "{IP}", Wide.IP, 1)
	Wide.OutputChannel = strings.Replace(Wide.OutputChannel, "{IP}", Wide.IP, 1)
	Wide.ShellChannel = strings.Replace(Wide.ShellChannel, "{IP}", Wide.IP, 1)
	Wide.SessionChannel = strings.Replace(Wide.SessionChannel, "{IP}", Wide.IP, 1)

	glog.V(5).Info("Conf: \n" + string(bytes))

	initWorkspaceDirs()
	initCustomizedConfs()
}

// initCustomizedConfs initializes the user customized configurations.
func initCustomizedConfs() {
	for _, user := range Wide.Users {
		UpdateCustomizedConf(user.Name)
	}
}

// UpdateCustomizedConf creates (if not exists) or updates user customized configuration files.
//
//  1. /static/user/{username}/style.css
func UpdateCustomizedConf(username string) {
	var u *User = nil
	for _, user := range Wide.Users { // maybe it is a beauty of the trade-off of the another world between design and implementation
		if user.Name == username {
			u = user
		}
	}

	if nil == u {
		return
	}

	model := map[string]interface{}{"user": u}

	t, err := template.ParseFiles("static/user/style.css.tmpl")
	if nil != err {
		glog.Error(err)

		os.Exit(-1)
	}

	wd := util.OS.Pwd()
	dir := filepath.Clean(wd + "/static/user/" + u.Name)
	if err := os.MkdirAll(dir, 0755); nil != err {
		glog.Error(err)

		os.Exit(-1)
	}

	fout, err := os.Create(dir + PathSeparator + "style.css")
	if nil != err {
		glog.Error(err)

		os.Exit(-1)
	}

	defer fout.Close()

	if err := t.Execute(fout, model); nil != err {
		glog.Error(err)

		os.Exit(-1)
	}
}

// initWorkspaceDirs initializes the directories of master workspace, users' workspaces.
//
// Creates directories if not found on path of workspace.
func initWorkspaceDirs() {
	paths := filepath.SplitList(Wide.GetWorkspace())

	for _, user := range Wide.Users {
		paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
	}

	for _, path := range paths {
		CreateWorkspaceDir(path)
	}
}

// CreateWorkspaceDir creates (if not exists) directories on the path.
//
//  1. root directory:{path}
//  2. src directory: {path}/src
//  3. package directory: {path}/pkg
//  4. binary directory: {path}/bin
func CreateWorkspaceDir(path string) {
	createDir(path)
	createDir(path + PathSeparator + "src")
	createDir(path + PathSeparator + "pkg")
	createDir(path + PathSeparator + "bin")
}

// createDir creates a directory on the path if it not exists.
func createDir(path string) {
	if !isExist(path) {
		if err := os.MkdirAll(path, 0775); nil != err {
			glog.Error(err)

			os.Exit(-1)
		}

		glog.V(7).Infof("Created a directory [%s]", path)
	}
}

// isExist determines whether the file spcified by the given filename is exists.
func isExist(filename string) bool {
	_, err := os.Stat(filename)

	return err == nil || os.IsExist(err)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"

	"github.com/jmoiron/sqlx"
	"github.com/labstack/echo"
	_ "github.com/lib/pq"
)

type Book struct {
	ID   int64  `db:"id" json:"id"`
	Name string `db:"name" json:"name"`
}

func createBook(db *sqlx.DB) func(c *echo.Context) error {
	return func(c *echo.Context) error {
		_, err := db.Exec(
			"INSERT INTO books(id, name) VALUES(nextval('books_seq'), $1)",
			c.Param("name"),
		)
		if err != nil {
			return c.NoContent(http.StatusBadRequest)
		}
		return c.NoContent(http.StatusOK)
	}
}

func listBooks(db *sqlx.DB) func(c *echo.Context) error {
	return func(c *echo.Context) error {
		var books []Book
		err := db.Select(&books, "SELECT * FROM books")
		if err != nil {
			return c.NoContent(http.StatusBadRequest)
		}
		return c.JSON(http.StatusOK, books)
	}
}

func newConnectionPool(url string) *sqlx.DB {
	db, err := sqlx.Open("postgres", url)
	if err != nil {
		log.Fatal(err)
	}
	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
	return db
}

func server(db *sqlx.DB) *echo.Echo {
	e := echo.New()
	e.Post("/books/:name", createBook(db))
	e.Get("/books", listBooks(db))
	return e
}

func main() {
	db := newConnectionPool("postgres://localhost:5432/example")
	e := server(db)
	e.Server(":3000")
}
<commit_msg>fix example<commit_after>package main

import (
	"log"
	"net/http"

	"github.com/jmoiron/sqlx"
	"github.com/labstack/echo"
	_ "github.com/lib/pq"
)

type Book struct {
	ID   int64  `db:"id" json:"id"`
	Name string `db:"name" json:"name"`
}

func createBook(db *sqlx.DB) func(c *echo.Context) error {
	return func(c *echo.Context) error {
		_, err := db.Exec(
			"INSERT INTO books(id, name) VALUES(nextval('books_seq'), $1)",
			c.Param("name"),
		)
		if err != nil {
			return c.NoContent(http.StatusBadRequest)
		}
		return c.NoContent(http.StatusOK)
	}
}

func listBooks(db *sqlx.DB) func(c *echo.Context) error {
	return func(c *echo.Context) error {
		var books []Book
		err := db.Select(&books, "SELECT * FROM books")
		if err != nil {
			return c.NoContent(http.StatusBadRequest)
		}
		return c.JSON(http.StatusOK, books)
	}
}

func newConnectionPool(url string) *sqlx.DB {
	db, err := sqlx.Open("postgres", url)
	if err != nil {
		log.Fatal(err)
	}
	if err := db.Ping(); err != nil {
		log.Fatal(err)
	}
	return db
}

func server(db *sqlx.DB) *echo.Echo {
	e := echo.New()
	e.Post("/books/:name", createBook(db))
	e.Get("/books", listBooks(db))
	return e
}

func main() {
	db := newConnectionPool("postgres://localhost:5432/example?sslmode=disable")
	e := server(db)
	e.Run(":3000")
}
<|endoftext|>


<commit_before>package main

import (
	"runtime"

	"github.com/smartystreets/go-disruptor"
)

const MaxConsumers = 1

func main() {
	runtime.GOMAXPROCS(MaxConsumers + 2)

	writerCursor := disruptor.NewCursor()
	writerBarrier := disruptor.NewBarrier(writerCursor)

	readerCursors1 := startReaders(writerBarrier, writerCursor)
	readerBarrier1 := disruptor.NewBarrier(readerCursors1...)

	readerCursors2 := startReaders(readerBarrier1, writerCursor)
	readerBarrier2 := disruptor.NewBarrier(readerCursors2...)

	writer := disruptor.NewWriter(writerCursor, RingSize, readerBarrier2)
	publish(writer)
}
func startReaders(upstreamBarrier disruptor.Barrier, writerCursor *disruptor.Cursor) (readerCursors []*disruptor.Cursor) {
	for i := 0; i < MaxConsumers; i++ {
		readerCursor := disruptor.NewCursor()
		readerCursors = append(readerCursors, readerCursor)
		reader := disruptor.NewReader(upstreamBarrier, writerCursor, readerCursor)

		// wildly sporadic latency for single-item publish, e.g. 2 seconds, 65 ms, etc.
		// faster for 2-3+ items per publish
		go consume(reader)

		// constant time regardless of the number of items
		// go easyConsume(disruptor.NewEasyReader(reader, NewExampleConsumerHandler()))
	}

	return readerCursors
}
<commit_msg>3 layers of readers.<commit_after>package main

import (
	"runtime"

	"github.com/smartystreets/go-disruptor"
)

const MaxConsumers = 1

func main() {
	runtime.GOMAXPROCS(MaxConsumers + 3)

	writerCursor := disruptor.NewCursor()
	writerBarrier := disruptor.NewBarrier(writerCursor)

	readerCursors1 := startReaders(writerBarrier, writerCursor)
	readerBarrier1 := disruptor.NewBarrier(readerCursors1...)

	readerCursors2 := startReaders(readerBarrier1, writerCursor)
	readerBarrier2 := disruptor.NewBarrier(readerCursors2...)

	readerCursors3 := startReaders(readerBarrier2, writerCursor)
	readerBarrier3 := disruptor.NewBarrier(readerCursors3...)

	writer := disruptor.NewWriter(writerCursor, RingSize, readerBarrier3)
	publish(writer)
}
func startReaders(upstreamBarrier disruptor.Barrier, writerCursor *disruptor.Cursor) (readerCursors []*disruptor.Cursor) {
	for i := 0; i < MaxConsumers; i++ {
		readerCursor := disruptor.NewCursor()
		readerCursors = append(readerCursors, readerCursor)
		reader := disruptor.NewReader(upstreamBarrier, writerCursor, readerCursor)

		// wildly sporadic latency for single-item publish, e.g. 2 seconds, 65 ms, etc.
		// faster for 2-3+ items per publish
		go consume(reader)

		// constant time regardless of the number of items
		// go easyConsume(disruptor.NewEasyReader(reader, NewExampleConsumerHandler()))
	}

	return readerCursors
}
<|endoftext|>


<commit_before>package configeur

import "fmt"

// Checker is the interface which external "checkers" must comply to. If a
// retrieval fails the next Checker in the Configeur.stack stack will be called.
// Checker's are added to the stack through the configeur.Use method.
type Checker interface {
	// Int finds and retrieves an integer by name. If it finds the value it will
	// return it, and if it doesn't an error will be returned and the next Checker
	// in the stack will be called.
	Int(name string) (int, error)

	// String finds and retrieves an string by name. If it finds the value it will
	// return it, and if it doesn't an error will be returned and the next Checker
	// in the stack will be called.
	String(name string) (string, error)

	// Bool finds and retrieves a boolean value by name.
	Bool(name string) (bool, error)
}

// Configeur is a stack of Checkers which are used to retrieve configuration values. It aims
// to have a similar API as the flag package in the standard library. Checker's are evaluated
// in the same order they are added through the initalization and Configeur.Use functions.
type Configeur struct {
	options map[string]*option // A map of all of the provided options
	stack   []Checker          // A list of all the "middlewear" which is used to find a value
}

// Int declares an int value. A pointer is returned that will
// point to the value after c.Parse() has been called.
func (c *Configeur) Int(name string, def int, description string) *int {
	v := c.option(name, def, description, intType)
	i, ok := v.(*int)

	if !ok {
		fmt.Printf("could not retrieve pointer to value for name %v\n", name)
		return nil
	}

	return i
}

// String declares a string value. A pointer is returned that will
// point to the value after c.Parse() has been called.
func (c *Configeur) String(name, def, description string) *string {
	v := c.option(name, def, description, stringType)
	s, ok := v.(*string)

	if !ok {
		fmt.Printf("could not retrieve pointer to value for name %v\n", name)
		return nil
	}

	return s
}

// Bool declares a new boolean option. A pointer is returned to this
// value that will be filled after Configeur.Parse() has been called.
func (c *Configeur) Bool(name string, def bool, description string) *bool {
	v := c.option(name, def, description, boolType)
	b, ok := v.(*bool)

	if !ok {
		fmt.Printf("could not retrieve pointer to that value\n")
		return nil
	}

	return b
}

// option returns a pointer of a type specified through the valueType parameter.
//
// note: You could potentially find the value to be pointed to through the def
// parameter, but this would pose an issue when one is not provided.
func (c *Configeur) option(name string, def interface{}, description string, typ valueType) interface{} {
	opt := &option{
		name:        name,
		def:         def,
		description: description,
		typ:         typ,
	}

	c.options[name] = opt

	switch opt.typ {
	case stringType:
		opt.value = new(string)
	case intType:
		opt.value = new(int)
	case boolType:
		opt.value = new(bool)
	}

	return opt.value
}

// Use adds a variable amounts of new Checkers to the stack.
func (c *Configeur) Use(checkers ...Checker) {
	c.stack = append(c.stack, checkers...)
}

// Parse populates the pointers returned through the Configeur.Int and Configeur.String
// methods.
func (c *Configeur) Parse() {
	for _, opt := range c.options {
		changed := false
		for _, checker := range c.stack {
			switch opt.typ {
			case stringType:
				s, err := checker.String(opt.name)
				if err != nil {
					fmt.Println(err)
					continue
				}

				opt.set(s)
			case intType:
				i, err := checker.Int(opt.name)
				if err != nil {
					fmt.Println(err)
					continue
				}

				opt.set(i)
			case boolType:
				b, err := checker.Bool(opt.name)
				if err != nil {
					fmt.Println(err)
					continue
				}

				opt.set(b)
			}

			changed = true
			break
		}

		if !changed {
			opt.set(opt.def)
		}
	}
}

// New returns a pointer to a new Configeur instance with a stack
// provided through the variadic stack variable.
func New(stack ...Checker) *Configeur {
	c := &Configeur{
		options: make(map[string]*option),
		stack:   stack,
	}

	return c
}

type valueType int

func (vt valueType) String() string {
	name := ""
	switch vt {
	case intType:
		name = "Integer"
	case stringType:
		name = "String"
	case boolType:
		name = "Boolean"
	default:
		return "NOT A valueType"
	}

	return name
}

const (
	intType valueType = iota + 1
	stringType
	boolType
)

type option struct {
	name        string
	description string
	def         interface{}
	value       interface{}
	typ         valueType
}

func (o option) String() string {
	return fmt.Sprintf("%v(%v)[%v]", o.name, o.description, o.typ)
}

func (o *option) set(value interface{}) {
	switch value.(type) {
	case bool:
		z, ok := o.value.(*bool)
		if !ok {
			fmt.Println("that var doesnt fit thy value")
		}

		*z = value.(bool)
	case int:
		z, ok := o.value.(*int)
		if !ok {
			fmt.Println("that var doesnt fit thy value")
		}

		*z = value.(int)

	case string:
		z, ok := o.value.(*string)
		if !ok {
			fmt.Println("that var doesnt fit thy value")
		}

		*z = value.(string)
	}
}
<commit_msg>Rewrite interface documentation<commit_after>package configeur

import "fmt"

// Checker is the interface which external checkers must comply to. In the configeur
// implementation, if one Checker fails the next one in the stack should be called.
type Checker interface {
	// Int attempts to get an int value from the data source.
	Int(name string) (int, error)

	// String attempts to get a string value from the data source.
	String(name string) (string, error)

	// Bool attempts to get a bool value from the data source.
	Bool(name string) (bool, error)
}

// Configeur is a stack of Checkers which are used to retrieve configuration values. It has a
// similar API as the flag package in the standard library, and is also partially
// inspired by negroni. Checkers are evaluated in the same order they are added.
type Configeur struct {
	options map[string]*option // A map of all of the provided options
	stack   []Checker          // A list of all the "middlewear" which is used to find a value
}

// Int defines an int flag with a name, default and description. The return value
// is a pointer which will be populated with the value of the flag.
func (c *Configeur) Int(name string, def int, description string) *int {
	v := c.option(name, def, description, intType)
	i, ok := v.(*int)

	if !ok {
		fmt.Printf("could not retrieve pointer to value for name %v\n", name)
		return nil
	}

	return i
}

// String defines a string flag with a name, default and description. The return value
// is a pointer which will be populated with the value of the flag.
func (c *Configeur) String(name, def, description string) *string {
	v := c.option(name, def, description, stringType)
	s, ok := v.(*string)

	if !ok {
		fmt.Printf("could not retrieve pointer to value for name %v\n", name)
		return nil
	}

	return s
}

// Bool defines a bool flag with a name, default and description. The return value
// is a pointer which will be populated with the value of the flag.
func (c *Configeur) Bool(name string, def bool, description string) *bool {
	v := c.option(name, def, description, boolType)
	b, ok := v.(*bool)

	if !ok {
		fmt.Printf("could not retrieve pointer to that value\n")
		return nil
	}

	return b
}

// option returns a pointer of a type specified through the valueType parameter.
//
// note: You could potentially find the value to be pointed to through the def
// parameter, but this would pose an issue when one is not provided.
func (c *Configeur) option(name string, def interface{}, description string, typ valueType) interface{} {
	opt := &option{
		name:        name,
		def:         def,
		description: description,
		typ:         typ,
	}

	c.options[name] = opt

	switch opt.typ {
	case stringType:
		opt.value = new(string)
	case intType:
		opt.value = new(int)
	case boolType:
		opt.value = new(bool)
	}

	return opt.value
}

// Use adds a variable amount of Checkers onto the stack.
func (c *Configeur) Use(checkers ...Checker) {
	c.stack = append(c.stack, checkers...)
}

// Parse populates all of the defined arguments with their values provided by
// the stacks Checkers.
func (c *Configeur) Parse() {
	for _, opt := range c.options {
		changed := false
		for _, checker := range c.stack {
			switch opt.typ {
			case stringType:
				s, err := checker.String(opt.name)
				if err != nil {
					fmt.Println(err)
					continue
				}

				opt.set(s)
			case intType:
				i, err := checker.Int(opt.name)
				if err != nil {
					fmt.Println(err)
					continue
				}

				opt.set(i)
			case boolType:
				b, err := checker.Bool(opt.name)
				if err != nil {
					fmt.Println(err)
					continue
				}

				opt.set(b)
			}

			changed = true
			break
		}

		if !changed {
			opt.set(opt.def)
		}
	}
}

// New returns a pointer to a new Configeur instance with a stack
// provided through the variadic stack variable.
func New(stack ...Checker) *Configeur {
	c := &Configeur{
		options: make(map[string]*option),
		stack:   stack,
	}

	return c
}

type valueType int

func (vt valueType) String() string {
	name := ""
	switch vt {
	case intType:
		name = "Integer"
	case stringType:
		name = "String"
	case boolType:
		name = "Boolean"
	default:
		return "NOT A valueType"
	}

	return name
}

const (
	intType valueType = iota + 1
	stringType
	boolType
)

type option struct {
	name        string
	description string
	def         interface{}
	value       interface{}
	typ         valueType
}

func (o option) String() string {
	return fmt.Sprintf("%v(%v)[%v]", o.name, o.description, o.typ)
}

func (o *option) set(value interface{}) {
	switch value.(type) {
	case bool:
		z, ok := o.value.(*bool)
		if !ok {
			fmt.Println("that var doesnt fit thy value")
		}

		*z = value.(bool)
	case int:
		z, ok := o.value.(*int)
		if !ok {
			fmt.Println("that var doesnt fit thy value")
		}

		*z = value.(int)

	case string:
		z, ok := o.value.(*string)
		if !ok {
			fmt.Println("that var doesnt fit thy value")
		}

		*z = value.(string)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Tony Bai.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package cmppconn

import (
	"encoding/binary"
	"io"
	"net"

	cmpppacket "github.com/bigwhite/gocmpp/packet"
)

type State uint8

// Conn States
const (
	CONN_CLOSED = iota
	CONN_CONNECTED
	CONN_AUTHOK
)

type Conn struct {
	net.Conn
	State State
	Typ   cmpppacket.Type
	SeqId <-chan uint32
	done  chan<- struct{}
}

func newSeqIdGenerator() (<-chan uint32, chan<- struct{}) {
	out := make(chan uint32)
	done := make(chan struct{})

	go func() {
		var i uint32
		for {
			select {
			case out <- i:
				i++
			case <-done:
				close(out)
				return
			}
		}
	}()
	return out, done
}

func New(conn net.Conn, typ cmpppacket.Type) *Conn {
	seqId, done := newSeqIdGenerator()
	c := &Conn{
		Conn:  conn,
		Typ:   typ,
		State: CONN_CONNECTED,
		SeqId: seqId,
		done:  done,
	}
	tc := c.Conn.(*net.TCPConn) // Always tcpconn
	tc.SetKeepAlive(true)
	return c
}

func (c *Conn) Close() {
	if c != nil {
		if c.Typ == CONN_CLOSED {
			return
		}
		close(c.done)
		c.Conn.Close()
		c.Typ = CONN_CLOSED
		c = nil
	}
}

func (c *Conn) SetState(state State) {
	c.State = state
}

// SendPkt pack the cmpp packet structure and send it to the other peer.
func (c *Conn) SendPkt(packet cmpppacket.Packer, seqId uint32) error {
	data, err := packet.Pack(seqId)
	if err != nil {
		return err
	}

	_, err = c.Conn.Write(data) //block write
	if err != nil {
		return err
	}

	return nil
}

// RecvAndUnpackPkt receives cmpp byte stream, and unpack it to some cmpp packet structure.
func (c *Conn) RecvAndUnpackPkt() (interface{}, error) {
	// Total_Length in packet
	var totalLen uint32
	err := binary.Read(c.Conn, binary.BigEndian, &totalLen)
	if err != nil {
		return nil, err
	}

	if c.Typ == cmpppacket.V30 {
		if totalLen < cmpppacket.CMPP3_PACKET_MIN || totalLen > cmpppacket.CMPP3_PACKET_MAX {
			return nil, cmpppacket.ErrTotalLengthInvalid
		}
	}

	if c.Typ == cmpppacket.V21 || c.Typ == cmpppacket.V20 {
		if totalLen < cmpppacket.CMPP2_PACKET_MIN || totalLen > cmpppacket.CMPP2_PACKET_MAX {
			return nil, cmpppacket.ErrTotalLengthInvalid
		}
	}

	// Command_Id
	var commandId cmpppacket.CommandId
	err = binary.Read(c.Conn, binary.BigEndian, &commandId)
	if err != nil {
		return nil, err
	}

	if !((commandId > cmpppacket.CMPP_REQUEST_MIN && commandId < cmpppacket.CMPP_REQUEST_MAX) ||
		(commandId > cmpppacket.CMPP_RESPONSE_MIN && commandId < cmpppacket.CMPP_RESPONSE_MAX)) {
		return nil, cmpppacket.ErrCommandIdInvalid
	}

	// The left packet data (start from seqId in header).
	var leftData = make([]byte, totalLen-8)
	_, err = io.ReadFull(c.Conn, leftData)
	if err != nil {
		return nil, err
	}

	var p cmpppacket.Packer
	switch commandId {
	case cmpppacket.CMPP_CONNECT:
		p = &cmpppacket.CmppConnReqPkt{}
	case cmpppacket.CMPP_CONNECT_RESP:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3ConnRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2ConnRspPkt{}
		}
	case cmpppacket.CMPP_TERMINATE:
		p = &cmpppacket.CmppTerminateReqPkt{}
	case cmpppacket.CMPP_TERMINATE_RESP:
		p = &cmpppacket.CmppTerminateRspPkt{}
	case cmpppacket.CMPP_SUBMIT:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3SubmitReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2SubmitReqPkt{}
		}
	case cmpppacket.CMPP_SUBMIT_RESP:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3SubmitRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2SubmitRspPkt{}
		}
	case cmpppacket.CMPP_DELIVER:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3DeliverReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2DeliverReqPkt{}
		}
	case cmpppacket.CMPP_DELIVER_RESP:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3DeliverRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2DeliverRspPkt{}
		}
	case cmpppacket.CMPP_FWD:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3FwdReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2FwdReqPkt{}
		}
	case cmpppacket.CMPP_FWD_RESP:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3FwdRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2FwdRspPkt{}
		}
	case cmpppacket.CMPP_ACTIVE_TEST:
		p = &cmpppacket.CmppActiveTestReqPkt{}
	case cmpppacket.CMPP_ACTIVE_TEST_RESP:
		p = &cmpppacket.CmppActiveTestRspPkt{}

	default:
		p = nil
		return nil, cmpppacket.ErrCommandIdNotSupported
	}

	err = p.Unpack(leftData)
	if err != nil {
		return nil, err
	}
	return p, nil
}
<commit_msg>fix issue that misusing State and Typ<commit_after>// Copyright 2015 Tony Bai.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package cmppconn

import (
	"encoding/binary"
	"io"
	"net"

	cmpppacket "github.com/bigwhite/gocmpp/packet"
)

type State uint8

// Conn States
const (
	CONN_CLOSED State = iota
	CONN_CONNECTED
	CONN_AUTHOK
)

type Conn struct {
	net.Conn
	State State
	Typ   cmpppacket.Type

	// for SeqId generator goroutine
	SeqId <-chan uint32
	done  chan<- struct{}
}

func newSeqIdGenerator() (<-chan uint32, chan<- struct{}) {
	out := make(chan uint32)
	done := make(chan struct{})

	go func() {
		var i uint32
		for {
			select {
			case out <- i:
				i++
			case <-done:
				close(out)
				return
			}
		}
	}()
	return out, done
}

func New(conn net.Conn, typ cmpppacket.Type) *Conn {
	seqId, done := newSeqIdGenerator()
	c := &Conn{
		Conn:  conn,
		Typ:   typ,
		State: CONN_CONNECTED,
		SeqId: seqId,
		done:  done,
	}
	tc := c.Conn.(*net.TCPConn) // Always tcpconn
	tc.SetKeepAlive(true)
	return c
}

func (c *Conn) Close() {
	if c != nil {
		if c.State == CONN_CLOSED {
			return
		}
		close(c.done)
		c.Conn.Close()
		c.State = CONN_CLOSED
		c = nil
	}
}

func (c *Conn) SetState(state State) {
	c.State = state
}

// SendPkt pack the cmpp packet structure and send it to the other peer.
func (c *Conn) SendPkt(packet cmpppacket.Packer, seqId uint32) error {
	data, err := packet.Pack(seqId)
	if err != nil {
		return err
	}

	_, err = c.Conn.Write(data) //block write
	if err != nil {
		return err
	}

	return nil
}

// RecvAndUnpackPkt receives cmpp byte stream, and unpack it to some cmpp packet structure.
func (c *Conn) RecvAndUnpackPkt() (interface{}, error) {
	// Total_Length in packet
	var totalLen uint32
	err := binary.Read(c.Conn, binary.BigEndian, &totalLen)
	if err != nil {
		return nil, err
	}

	if c.Typ == cmpppacket.V30 {
		if totalLen < cmpppacket.CMPP3_PACKET_MIN || totalLen > cmpppacket.CMPP3_PACKET_MAX {
			return nil, cmpppacket.ErrTotalLengthInvalid
		}
	}

	if c.Typ == cmpppacket.V21 || c.Typ == cmpppacket.V20 {
		if totalLen < cmpppacket.CMPP2_PACKET_MIN || totalLen > cmpppacket.CMPP2_PACKET_MAX {
			return nil, cmpppacket.ErrTotalLengthInvalid
		}
	}

	// Command_Id
	var commandId cmpppacket.CommandId
	err = binary.Read(c.Conn, binary.BigEndian, &commandId)
	if err != nil {
		return nil, err
	}

	if !((commandId > cmpppacket.CMPP_REQUEST_MIN && commandId < cmpppacket.CMPP_REQUEST_MAX) ||
		(commandId > cmpppacket.CMPP_RESPONSE_MIN && commandId < cmpppacket.CMPP_RESPONSE_MAX)) {
		return nil, cmpppacket.ErrCommandIdInvalid
	}

	// The left packet data (start from seqId in header).
	var leftData = make([]byte, totalLen-8)
	_, err = io.ReadFull(c.Conn, leftData)
	if err != nil {
		return nil, err
	}

	var p cmpppacket.Packer
	switch commandId {
	case cmpppacket.CMPP_CONNECT:
		p = &cmpppacket.CmppConnReqPkt{}
	case cmpppacket.CMPP_CONNECT_RESP:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3ConnRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2ConnRspPkt{}
		}
	case cmpppacket.CMPP_TERMINATE:
		p = &cmpppacket.CmppTerminateReqPkt{}
	case cmpppacket.CMPP_TERMINATE_RESP:
		p = &cmpppacket.CmppTerminateRspPkt{}
	case cmpppacket.CMPP_SUBMIT:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3SubmitReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2SubmitReqPkt{}
		}
	case cmpppacket.CMPP_SUBMIT_RESP:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3SubmitRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2SubmitRspPkt{}
		}
	case cmpppacket.CMPP_DELIVER:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3DeliverReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2DeliverReqPkt{}
		}
	case cmpppacket.CMPP_DELIVER_RESP:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3DeliverRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2DeliverRspPkt{}
		}
	case cmpppacket.CMPP_FWD:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3FwdReqPkt{}
		} else {
			p = &cmpppacket.Cmpp2FwdReqPkt{}
		}
	case cmpppacket.CMPP_FWD_RESP:
		if c.Typ == cmpppacket.V30 {
			p = &cmpppacket.Cmpp3FwdRspPkt{}
		} else {
			p = &cmpppacket.Cmpp2FwdRspPkt{}
		}
	case cmpppacket.CMPP_ACTIVE_TEST:
		p = &cmpppacket.CmppActiveTestReqPkt{}
	case cmpppacket.CMPP_ACTIVE_TEST_RESP:
		p = &cmpppacket.CmppActiveTestRspPkt{}

	default:
		p = nil
		return nil, cmpppacket.ErrCommandIdNotSupported
	}

	err = p.Unpack(leftData)
	if err != nil {
		return nil, err
	}
	return p, nil
}
<|endoftext|>


<commit_before>package conn_test

import (
	"bytes"
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/gorilla/websocket"
	"github.com/shiwano/websocket-conn"
)

func newTestServer(handler func(http.ResponseWriter, *http.Request)) (*httptest.Server, string) {
	ts := httptest.NewServer(http.HandlerFunc(handler))
	url := strings.Replace(ts.URL, "http", "ws", 1)
	return ts, url
}

func TestConn(t *testing.T) {
	serverConnCh := make(chan error)
	rootCtx := context.Background()

	ts, url := newTestServer(func(w http.ResponseWriter, r *http.Request) {
		c, err := conn.UpgradeFromHTTP(rootCtx, conn.DefaultSettings(), w, r)
		if err != nil {
			t.Error(err)
		}

		d := <-c.Stream()
		c.SendTextMessage(d.Message.Text() + " PONG")
		d = <-c.Stream()
		c.SendBinaryMessage(append(d.Message.Data, 4, 5, 6))

		for d := range c.Stream() {
			if !d.EOS {
				t.Error(fmt.Errorf("Received an unexpected data to the server connetion: %v", d))
			}
		}
		serverConnCh <- c.Err()
	})
	defer ts.Close()

	ctx, cancel := context.WithCancel(rootCtx)
	c, _, err := conn.Connect(ctx, conn.DefaultSettings(), url, nil)
	if err != nil {
		t.Error(err)
	}

	c.SendTextMessage("PING")
	d := <-c.Stream()
	if d.Message.MessageType != conn.TextMessageType || d.Message.Text() != "PING PONG" {
		t.Error(fmt.Errorf("Failed to process a text message: %v", d))
	}

	c.SendBinaryMessage([]byte{1, 2, 3})
	d = <-c.Stream()
	if d.Message.MessageType != conn.BinaryMessageType || !bytes.Equal(d.Message.Data, []byte{1, 2, 3, 4, 5, 6}) {
		t.Error(fmt.Errorf("Failed to process a binary message: %v", d))
	}

	cancel()

	serverConnErr := <-serverConnCh
	if serverConnErr.(*websocket.CloseError).Code != websocket.CloseNormalClosure {
		t.Error(fmt.Errorf("Unexpected server connection error: %v", serverConnErr))
	}

	for d := range c.Stream() {
		if !d.EOS {
			t.Error(fmt.Errorf("Received an unexpected data to the client connetion: %v", d))
		}
	}
	if c.Err().Error() != "context canceled" {
		t.Error(fmt.Errorf("Unexpected client connection error: %v", c.Err()))
	}
}
<commit_msg>Tweak test<commit_after>package conn_test

import (
	"bytes"
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/gorilla/websocket"
	"github.com/shiwano/websocket-conn"
)

func newTestServer(handler func(http.ResponseWriter, *http.Request)) (*httptest.Server, string) {
	ts := httptest.NewServer(http.HandlerFunc(handler))
	url := strings.Replace(ts.URL, "http", "ws", 1)
	return ts, url
}

func TestConn(t *testing.T) {
	serverConnCh := make(chan error)
	rootCtx := context.Background()

	ts, url := newTestServer(func(w http.ResponseWriter, r *http.Request) {
		c, err := conn.UpgradeFromHTTP(rootCtx, conn.DefaultSettings(), w, r)
		if err != nil {
			t.Error(err)
		}

		d := <-c.Stream()
		c.SendTextMessage(d.Message.Text() + " PONG")
		d = <-c.Stream()
		c.SendBinaryMessage(append(d.Message.Data, 4, 5, 6))

		for d := range c.Stream() {
			if !d.EOS {
				t.Error(fmt.Errorf("Received an unexpected data to the server connetion: %v", d))
			}
		}
		serverConnCh <- c.Err()
	})
	defer ts.Close()

	ctx, cancel := context.WithCancel(rootCtx)
	c, _, err := conn.Connect(ctx, conn.DefaultSettings(), url, nil)
	if err != nil {
		t.Error(err)
	}

	c.SendTextMessage("PING")
	d := <-c.Stream()
	if d.Message.MessageType != conn.TextMessageType || d.Message.Text() != "PING PONG" {
		t.Error(fmt.Errorf("Failed to process a text message: %v", d))
	}

	c.SendBinaryMessage([]byte{1, 2, 3})
	d = <-c.Stream()
	if d.Message.MessageType != conn.BinaryMessageType || !bytes.Equal(d.Message.Data, []byte{1, 2, 3, 4, 5, 6}) {
		t.Error(fmt.Errorf("Failed to process a binary message: %v", d))
	}

	cancel()

	serverConnErr := <-serverConnCh
	if serverConnErr.(*websocket.CloseError).Code != websocket.CloseNormalClosure {
		t.Error(fmt.Errorf("Unexpected server connection error: %v", serverConnErr))
	}

	for d := range c.Stream() {
		if !d.EOS {
			t.Error(fmt.Errorf("Received an unexpected data to the client connetion: %v", d))
		}
	}
	if c.Err() != context.Canceled {
		t.Error(fmt.Errorf("Unexpected client connection error: %v", c.Err()))
	}
}
<|endoftext|>


<commit_before>package goetty

import (
	"errors"
	"net"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	// ErrWrite write error
	ErrWrite = errors.New("goetty.net: Write failed")
	// ErrEmptyServers empty server error
	ErrEmptyServers = errors.New("goetty.Connector: Empty servers pool")
	// ErrIllegalState illegal state error
	ErrIllegalState = errors.New("goetty.Connector: Not connected")
)

// Conf config for connector
type Conf struct {
	Addr                   string
	TimeoutConnectToServer time.Duration
	TimeWheel              *TimeoutWheel
	TimeoutWrite           time.Duration
	WriteTimeoutFn         func(string, IOSession)
}

type connector struct {
	sync.RWMutex

	cnf   *Conf
	attrs map[string]interface{}

	lastTimeout Timeout

	conn         net.Conn
	decoder      Decoder
	encoder      Encoder
	in           *ByteBuf
	out          *ByteBuf
	closed       int32
	writeBufSize int

	batchLimit uint64
	batchCount uint64
}

// NewConnector create a new connector
func NewConnector(cnf *Conf, decoder Decoder, encoder Encoder) IOSession {
	return NewConnectorSize(cnf, decoder, encoder, BufReadSize, BufWriteSize)
}

// NewConnectorSize create a new connector
func NewConnectorSize(cnf *Conf, decoder Decoder, encoder Encoder, readBufSize, writeBufSize int) IOSession {
	return &connector{
		cnf:          cnf,
		in:           NewByteBuf(readBufSize),
		out:          NewByteBuf(writeBufSize),
		writeBufSize: writeBufSize,
		decoder:      decoder,
		encoder:      encoder,
		attrs:        make(map[string]interface{}),
	}
}

// InBuf returns internal bytebuf that used for read from client
func (c *connector) InBuf() *ByteBuf {
	return c.in
}

// OutBuf returns internal bytebuf that used for write to client
func (c *connector) OutBuf() *ByteBuf {
	return c.out
}

// WriteOutBuf writes bytes that in the internal bytebuf
func (c *connector) WriteOutBuf() error {
	buf := c.out
	n, err := c.conn.Write(buf.buf[buf.readerIndex:buf.writerIndex])
	c.batchCount = 0

	if err != nil {
		c.writeRelease()
		return err
	}

	if n != buf.Readable() {
		c.writeRelease()
		return ErrWrite
	}

	c.writeRelease()
	return nil
}

// SetAttr add a attr on session
func (c *connector) SetAttr(key string, value interface{}) {
	c.Lock()
	c.attrs[key] = value
	c.Unlock()
}

// GetAttr get attr from session
func (c *connector) GetAttr(key string) interface{} {
	c.RLock()
	v := c.attrs[key]
	c.RUnlock()
	return v
}

// ID get id
func (c *connector) ID() interface{} {
	return 0
}

// Hash get hash value use id
func (c *connector) Hash() int {
	return 0
}

func (c *connector) Connect() (bool, error) {
	e := c.Close() // Close current connection

	if e != nil {
		return false, e
	}

	conn, e := net.DialTimeout("tcp", c.cnf.Addr, c.cnf.TimeoutConnectToServer)

	if nil != e {
		return false, e
	}

	conn.(*net.TCPConn).SetNoDelay(true)
	conn.(*net.TCPConn).SetLinger(0)
	c.conn = conn
	atomic.StoreInt32(&c.closed, 0)
	c.bindWriteTimeout()

	return true, nil
}

// Close close
func (c *connector) Close() error {
	if nil != c.conn {
		err := c.conn.Close()
		if err != nil {
			c.reset()
			return err
		}
	}

	c.reset()
	return nil
}

// IsConnected is connected
func (c *connector) IsConnected() bool {
	return nil != c.conn && atomic.LoadInt32(&c.closed) == 0
}

func (c *connector) reset() {
	atomic.StoreInt32(&c.closed, 1)
	c.conn = nil
}

// Read read data from server, block until a msg arrived or  get a error
func (c *connector) Read() (interface{}, error) {
	return c.ReadTimeout(0)
}

// ReadTimeout read data from server with a timeout duration
func (c *connector) ReadTimeout(timeout time.Duration) (interface{}, error) {
	if !c.IsConnected() {
		return nil, ErrIllegalState
	}

	var msg interface{}
	var err error
	var complete bool

	for {
		if c.in.Readable() > 0 {
			complete, msg, err = c.decoder.Decode(c.in)

			if !complete && err == nil {
				complete, msg, err = c.readFromConn(timeout)
			}
		} else {
			complete, msg, err = c.readFromConn(timeout)
		}

		if nil != err {
			c.in.Clear()
			return nil, err
		}

		if complete {
			break
		}
	}

	if c.in.Readable() == 0 {
		c.in.Clear()
	}

	return msg, err
}

// Write write a msg to server
func (c *connector) Write(msg interface{}) error {
	err := c.encoder.Encode(msg, c.out)

	if err != nil {
		return err
	}

	return c.WriteOutBuf()
}

func (c *connector) SetBatchSize(size uint64) {
	c.batchLimit = size
}

func (c *connector) WriteBatch(msg interface{}) error {
	err := c.encoder.Encode(msg, c.out)

	if err != nil {
		return err
	}

	c.batchCount++

	if c.batchCount%c.batchLimit == 0 {
		return c.WriteOutBuf()
	}

	return nil
}

// RemoteAddr get remote address
func (c *connector) RemoteAddr() string {
	if nil != c.conn {
		return c.conn.RemoteAddr().String()
	}

	return ""
}

// RemoteIP return remote ip address
func (c *connector) RemoteIP() string {
	addr := c.RemoteAddr()
	if addr == "" {
		return ""
	}

	return strings.Split(addr, ":")[0]
}

func (c *connector) readFromConn(timeout time.Duration) (bool, interface{}, error) {
	if 0 != timeout {
		c.conn.SetReadDeadline(time.Now().Add(timeout))
	}

	_, err := c.in.ReadFrom(c.conn)

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

	return c.decoder.Decode(c.in)
}

func (c *connector) writeRelease() {
	c.out.Clear()
	c.bindWriteTimeout()
}

func (c *connector) bindWriteTimeout() {
	if c.cnf.WriteTimeoutFn != nil {
		c.lastTimeout, _ = c.cnf.TimeWheel.Schedule(c.cnf.TimeoutWrite, c.writeTimeout, nil)
	}
}

func (c *connector) cancelWriteTimeout() {
	if c.cnf.WriteTimeoutFn != nil {
		c.lastTimeout.Stop()
	}
}

func (c *connector) writeTimeout(arg interface{}) {
	if c.cnf.WriteTimeoutFn != nil {
		c.cnf.WriteTimeoutFn(c.cnf.Addr, c)
	}
}
<commit_msg>fix: connector not cancel write timeout when wirte a msg<commit_after>package goetty

import (
	"errors"
	"net"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	// ErrWrite write error
	ErrWrite = errors.New("goetty.net: Write failed")
	// ErrEmptyServers empty server error
	ErrEmptyServers = errors.New("goetty.Connector: Empty servers pool")
	// ErrIllegalState illegal state error
	ErrIllegalState = errors.New("goetty.Connector: Not connected")
)

// Conf config for connector
type Conf struct {
	Addr                   string
	TimeoutConnectToServer time.Duration
	TimeWheel              *TimeoutWheel
	TimeoutWrite           time.Duration
	WriteTimeoutFn         func(string, IOSession)
}

type connector struct {
	sync.RWMutex

	cnf   *Conf
	attrs map[string]interface{}

	lastTimeout Timeout

	conn         net.Conn
	decoder      Decoder
	encoder      Encoder
	in           *ByteBuf
	out          *ByteBuf
	closed       int32
	writeBufSize int

	batchLimit uint64
	batchCount uint64
}

// NewConnector create a new connector
func NewConnector(cnf *Conf, decoder Decoder, encoder Encoder) IOSession {
	return NewConnectorSize(cnf, decoder, encoder, BufReadSize, BufWriteSize)
}

// NewConnectorSize create a new connector
func NewConnectorSize(cnf *Conf, decoder Decoder, encoder Encoder, readBufSize, writeBufSize int) IOSession {
	return &connector{
		cnf:          cnf,
		in:           NewByteBuf(readBufSize),
		out:          NewByteBuf(writeBufSize),
		writeBufSize: writeBufSize,
		decoder:      decoder,
		encoder:      encoder,
		attrs:        make(map[string]interface{}),
	}
}

// InBuf returns internal bytebuf that used for read from client
func (c *connector) InBuf() *ByteBuf {
	return c.in
}

// OutBuf returns internal bytebuf that used for write to client
func (c *connector) OutBuf() *ByteBuf {
	return c.out
}

// WriteOutBuf writes bytes that in the internal bytebuf
func (c *connector) WriteOutBuf() error {
	buf := c.out
	n, err := c.conn.Write(buf.buf[buf.readerIndex:buf.writerIndex])
	c.batchCount = 0

	if err != nil {
		c.writeRelease()
		return err
	}

	if n != buf.Readable() {
		c.writeRelease()
		return ErrWrite
	}

	c.writeRelease()
	return nil
}

// SetAttr add a attr on session
func (c *connector) SetAttr(key string, value interface{}) {
	c.Lock()
	c.attrs[key] = value
	c.Unlock()
}

// GetAttr get attr from session
func (c *connector) GetAttr(key string) interface{} {
	c.RLock()
	v := c.attrs[key]
	c.RUnlock()
	return v
}

// ID get id
func (c *connector) ID() interface{} {
	return 0
}

// Hash get hash value use id
func (c *connector) Hash() int {
	return 0
}

func (c *connector) Connect() (bool, error) {
	e := c.Close() // Close current connection

	if e != nil {
		return false, e
	}

	conn, e := net.DialTimeout("tcp", c.cnf.Addr, c.cnf.TimeoutConnectToServer)

	if nil != e {
		return false, e
	}

	conn.(*net.TCPConn).SetNoDelay(true)
	conn.(*net.TCPConn).SetLinger(0)
	c.conn = conn
	atomic.StoreInt32(&c.closed, 0)
	c.bindWriteTimeout()

	return true, nil
}

// Close close
func (c *connector) Close() error {
	if nil != c.conn {
		err := c.conn.Close()
		if err != nil {
			c.reset()
			return err
		}
	}

	c.reset()
	return nil
}

// IsConnected is connected
func (c *connector) IsConnected() bool {
	return nil != c.conn && atomic.LoadInt32(&c.closed) == 0
}

func (c *connector) reset() {
	atomic.StoreInt32(&c.closed, 1)
	c.conn = nil
}

// Read read data from server, block until a msg arrived or  get a error
func (c *connector) Read() (interface{}, error) {
	return c.ReadTimeout(0)
}

// ReadTimeout read data from server with a timeout duration
func (c *connector) ReadTimeout(timeout time.Duration) (interface{}, error) {
	if !c.IsConnected() {
		return nil, ErrIllegalState
	}

	var msg interface{}
	var err error
	var complete bool

	for {
		if c.in.Readable() > 0 {
			complete, msg, err = c.decoder.Decode(c.in)

			if !complete && err == nil {
				complete, msg, err = c.readFromConn(timeout)
			}
		} else {
			complete, msg, err = c.readFromConn(timeout)
		}

		if nil != err {
			c.in.Clear()
			return nil, err
		}

		if complete {
			break
		}
	}

	if c.in.Readable() == 0 {
		c.in.Clear()
	}

	return msg, err
}

// Write write a msg to server
func (c *connector) Write(msg interface{}) error {
	err := c.encoder.Encode(msg, c.out)

	if err != nil {
		return err
	}

	return c.WriteOutBuf()
}

func (c *connector) SetBatchSize(size uint64) {
	c.batchLimit = size
}

func (c *connector) WriteBatch(msg interface{}) error {
	err := c.encoder.Encode(msg, c.out)

	if err != nil {
		return err
	}

	c.batchCount++

	if c.batchCount%c.batchLimit == 0 {
		return c.WriteOutBuf()
	}

	return nil
}

// RemoteAddr get remote address
func (c *connector) RemoteAddr() string {
	if nil != c.conn {
		return c.conn.RemoteAddr().String()
	}

	return ""
}

// RemoteIP return remote ip address
func (c *connector) RemoteIP() string {
	addr := c.RemoteAddr()
	if addr == "" {
		return ""
	}

	return strings.Split(addr, ":")[0]
}

func (c *connector) readFromConn(timeout time.Duration) (bool, interface{}, error) {
	if 0 != timeout {
		c.conn.SetReadDeadline(time.Now().Add(timeout))
	}

	_, err := c.in.ReadFrom(c.conn)

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

	return c.decoder.Decode(c.in)
}

func (c *connector) writeRelease() {
	c.out.Clear()
	c.bindWriteTimeout()
}

func (c *connector) bindWriteTimeout() {
	if c.cnf.WriteTimeoutFn != nil {
		c.lastTimeout.Stop()
		c.lastTimeout, _ = c.cnf.TimeWheel.Schedule(c.cnf.TimeoutWrite, c.writeTimeout, nil)
	}
}

func (c *connector) cancelWriteTimeout() {
	if c.cnf.WriteTimeoutFn != nil {
		c.lastTimeout.Stop()
	}
}

func (c *connector) writeTimeout(arg interface{}) {
	if c.cnf.WriteTimeoutFn != nil {
		c.cnf.WriteTimeoutFn(c.cnf.Addr, c)
	}
}
<|endoftext|>


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

package prototype

import (
	"fmt"
	"reflect"
	"sync"

	"github.com/golang/protobuf/v2/internal/value"
	"github.com/golang/protobuf/v2/reflect/protoreflect"
)

// GoEnum is a constructor for a protoreflect.EnumType.
type GoEnum struct {
	protoreflect.EnumDescriptor

	// New returns a concrete proto.Enum value with the given enum number.
	// The constructor must return the same concrete type for each invocation.
	New func(protoreflect.EnumType, protoreflect.EnumNumber) protoreflect.ProtoEnum

	once   sync.Once
	goType reflect.Type
}
type goEnum struct{ *GoEnum }

// NewGoEnum creates a new protoreflect.EnumType.
//
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields.
func NewGoEnum(t *GoEnum) protoreflect.EnumType {
	if t.IsPlaceholder() {
		panic("enum descriptor must not be a placeholder")
	}
	if t.New == nil {
		panic("invalid nil constructor for enum kind")
	}
	return goEnum{t}
}
func (p goEnum) GoNew(n protoreflect.EnumNumber) protoreflect.ProtoEnum {
	e := p.New(p, n)
	p.once.Do(func() { p.goType = reflect.TypeOf(e) })
	if p.goType != reflect.TypeOf(e) {
		panic(fmt.Sprintf("mismatching types for enum: got %T, want %v", e, p.goType))
	}
	return e
}
func (p goEnum) GoType() reflect.Type {
	p.once.Do(func() { p.goType = reflect.TypeOf(p.New(p, 0)) })
	return p.goType
}

// GoMessage is a constructor for a protoreflect.MessageType.
type GoMessage struct {
	protoreflect.MessageDescriptor

	// New returns a new empty proto.Message instance.
	// The constructor must return the same concrete type for each invocation.
	New func(protoreflect.MessageType) protoreflect.ProtoMessage

	once   sync.Once
	goType reflect.Type
}
type goMessage struct{ *GoMessage }

// NewGoMessage creates a new protoreflect.MessageType.
//
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields.
func NewGoMessage(t *GoMessage) protoreflect.MessageType {
	if t.IsPlaceholder() {
		panic("message descriptor must not be a placeholder")
	}
	if t.New == nil {
		panic("invalid nil constructor for message kind")
	}
	return goMessage{t}
}
func (p goMessage) GoNew() protoreflect.ProtoMessage {
	m := p.New(p)
	p.once.Do(func() { p.goType = reflect.TypeOf(m) })
	if p.goType != reflect.TypeOf(m) {
		panic(fmt.Sprintf("mismatching types for message: got %T, want %v", m, p.goType))
	}
	return m
}
func (p goMessage) GoType() reflect.Type {
	p.once.Do(func() { p.goType = reflect.TypeOf(p.New(p)) })
	return p.goType
}

// GoExtension is a constructor for a protoreflect.ExtensionType.
type GoExtension struct {
	protoreflect.ExtensionDescriptor

	// NewEnum returns a concrete proto.Enum value with the given enum number.
	// The constructor must be provided if protoreflect.ExtensionDescriptor.Kind
	// is protoreflect.EnumKind.
	//
	// The returned enum must represent an protoreflect.EnumDescriptor
	// that matches protoreflect.ExtensionDescriptor.EnumType.
	NewEnum func(protoreflect.EnumNumber) protoreflect.ProtoEnum

	// NewMessage returns a new empty proto.Message instance.
	// The constructor must be provided if protoreflect.ExtensionDescriptor.Kind
	// is protoreflect.MessageKind or protoreflect.GroupKind.
	//
	// The returned message must represent an protoreflect.MessageDescriptor
	// that matches protoreflect.ExtensionDescriptor.MessageType.
	NewMessage func() protoreflect.ProtoMessage

	// TODO: Separate NewEnum and NewMessage constructors make it possible for
	// users to provide a constructor that returns a Go type does not match
	// the corresponding protobuf descriptor in ExtensionDescriptor.
	// Checking for correctness is hard since descriptors are not comparable.
	//
	// An alternative API is for ExtensionDescriptor.{EnumType,MessageType}
	// to document that it must implement protoreflect.{EnumType,MessageType}.

	// TODO: Support custom Go types? If so, the user needs to provide their
	// own New, ValueOf, and InterfaceOf adapters.

	once        sync.Once
	new         func() interface{}
	goType      reflect.Type
	valueOf     func(v interface{}) protoreflect.Value
	interfaceOf func(v protoreflect.Value) interface{}
}
type goExtension struct{ *GoExtension }

// NewGoExtension creates a new protoreflect.ExtensionType.
//
// The Go type is currently determined automatically (although custom Go types
// may be supported in the future). The type is T for scalars and
// *[]T for vectors. Maps are not valid in extension fields.
// The type T is determined as follows:
//
//	+------------+-------------------------------------+
//	| Go type    | Protobuf kind                       |
//	+------------+-------------------------------------+
//	| bool       | BoolKind                            |
//	| int32      | Int32Kind, Sint32Kind, Sfixed32Kind |
//	| int64      | Int64Kind, Sint64Kind, Sfixed64Kind |
//	| uint32     | Uint32Kind, Fixed32Kind             |
//	| uint64     | Uint64Kind, Fixed64Kind             |
//	| float32    | FloatKind                           |
//	| float64    | DoubleKind                          |
//	| string     | StringKind                          |
//	| []byte     | BytesKind                           |
//	| E          | EnumKind                            |
//	| M          | MessageKind, GroupKind              |
//	+------------+-------------------------------------+
//
// The type E is the concrete enum type returned by NewEnum,
// which is often, but not required to be, a named int32 type.
// The type M is the concrete message type returned by NewMessage,
// which is often, but not required to be, a pointer to a named struct type.
//
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields.
func NewGoExtension(t *GoExtension) protoreflect.ExtensionType {
	if t.ExtendedType() == nil {
		panic("field descriptor does not extend a message")
	}
	switch t.Kind() {
	case protoreflect.EnumKind:
		if t.NewEnum == nil {
			panic("enum constructor not provided for enum kind")
		}
		if t.NewMessage != nil {
			panic("message constructor provided for enum kind")
		}
	case protoreflect.MessageKind, protoreflect.GroupKind:
		if t.NewMessage == nil {
			panic("message constructor not provided for message kind")
		}
		if t.NewEnum != nil {
			panic("enum constructor provided for message kind")
		}
	default:
		if t.NewMessage != nil || t.NewEnum != nil {
			panic(fmt.Sprintf("enum or message constructor provided for %v kind", t.Kind()))
		}
	}
	return goExtension{t}
}
func (p goExtension) GoNew() interface{} {
	p.lazyInit()
	v := p.new()
	if reflect.TypeOf(v) != p.goType {
		panic(fmt.Sprintf("invalid type: got %T, want %v", v, p.goType))
	}
	return v
}
func (p goExtension) GoType() reflect.Type {
	p.lazyInit()
	return p.goType
}
func (p goExtension) ValueOf(v interface{}) protoreflect.Value {
	p.lazyInit()
	if reflect.TypeOf(v) != p.goType {
		panic(fmt.Sprintf("invalid type: got %T, want %v", v, p.goType))
	}
	return p.valueOf(v)
}
func (p goExtension) InterfaceOf(pv protoreflect.Value) interface{} {
	p.lazyInit()
	v := p.interfaceOf(pv)
	if reflect.TypeOf(v) != p.goType {
		panic(fmt.Sprintf("invalid type: got %T, want %v", v, p.goType))
	}
	return v
}
func (p goExtension) lazyInit() {
	p.once.Do(func() {
		switch p.Cardinality() {
		case protoreflect.Optional:
			switch p.Kind() {
			case protoreflect.EnumKind:
				p.goType = reflect.TypeOf(p.NewEnum(0))
				p.new = func() interface{} { return p.NewEnum(p.Default().Enum()) }
				p.valueOf = func(v interface{}) protoreflect.Value {
					ev := v.(protoreflect.ProtoEnum).ProtoReflect()
					return protoreflect.ValueOf(ev.Number())
				}
				p.interfaceOf = func(pv protoreflect.Value) interface{} {
					return p.NewEnum(pv.Enum())
				}
			case protoreflect.MessageKind, protoreflect.GroupKind:
				p.goType = reflect.TypeOf(p.NewMessage())
				p.new = func() interface{} { return p.NewMessage() }
				p.valueOf = func(v interface{}) protoreflect.Value {
					return protoreflect.ValueOf(v)
				}
				p.interfaceOf = func(pv protoreflect.Value) interface{} {
					return pv.Message().Interface()
				}
			default:
				p.goType = goTypeForPBKind[p.Kind()]
				p.new = func() interface{} { return p.Default().Interface() }
				p.valueOf = func(v interface{}) protoreflect.Value {
					return protoreflect.ValueOf(v)
				}
				p.interfaceOf = func(pv protoreflect.Value) interface{} {
					v := pv.Interface()
					return v
				}
			}
		case protoreflect.Repeated:
			var goType reflect.Type
			switch p.Kind() {
			case protoreflect.EnumKind:
				goType = reflect.TypeOf(p.NewEnum(p.Default().Enum()))
			case protoreflect.MessageKind, protoreflect.GroupKind:
				goType = reflect.TypeOf(p.NewMessage())
			default:
				goType = goTypeForPBKind[p.Kind()]
			}
			c := value.NewConverter(goType, p.Kind())
			p.goType = reflect.PtrTo(reflect.SliceOf(goType))
			p.new = func() interface{} { return reflect.New(p.goType.Elem()).Interface() }
			p.valueOf = func(v interface{}) protoreflect.Value {
				return protoreflect.ValueOf(value.VectorOf(v, c))
			}
			p.interfaceOf = func(v protoreflect.Value) interface{} {
				// TODO: Can we assume that Vector implementations know how
				// to unwrap themselves?
				// Should this be part of the public API in protoreflect?
				return v.Vector().(value.Unwrapper).Unwrap()
			}
		default:
			panic(fmt.Sprintf("invalid cardinality: %v", p.Cardinality()))
		}
	})
}

var goTypeForPBKind = map[protoreflect.Kind]reflect.Type{
	protoreflect.BoolKind:     reflect.TypeOf(bool(false)),
	protoreflect.Int32Kind:    reflect.TypeOf(int32(0)),
	protoreflect.Sint32Kind:   reflect.TypeOf(int32(0)),
	protoreflect.Fixed32Kind:  reflect.TypeOf(int32(0)),
	protoreflect.Int64Kind:    reflect.TypeOf(int64(0)),
	protoreflect.Sint64Kind:   reflect.TypeOf(int64(0)),
	protoreflect.Fixed64Kind:  reflect.TypeOf(int64(0)),
	protoreflect.Uint32Kind:   reflect.TypeOf(uint32(0)),
	protoreflect.Sfixed32Kind: reflect.TypeOf(uint32(0)),
	protoreflect.Uint64Kind:   reflect.TypeOf(uint64(0)),
	protoreflect.Sfixed64Kind: reflect.TypeOf(uint64(0)),
	protoreflect.FloatKind:    reflect.TypeOf(float32(0)),
	protoreflect.DoubleKind:   reflect.TypeOf(float64(0)),
	protoreflect.StringKind:   reflect.TypeOf(string("")),
	protoreflect.BytesKind:    reflect.TypeOf([]byte(nil)),
}
<commit_msg>reflect/prototype: remove TODO about custom extensions constructors<commit_after>// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package prototype

import (
	"fmt"
	"reflect"
	"sync"

	"github.com/golang/protobuf/v2/internal/value"
	"github.com/golang/protobuf/v2/reflect/protoreflect"
)

// GoEnum is a constructor for a protoreflect.EnumType.
type GoEnum struct {
	protoreflect.EnumDescriptor

	// New returns a concrete proto.Enum value with the given enum number.
	// The constructor must return the same concrete type for each invocation.
	New func(protoreflect.EnumType, protoreflect.EnumNumber) protoreflect.ProtoEnum

	once   sync.Once
	goType reflect.Type
}
type goEnum struct{ *GoEnum }

// NewGoEnum creates a new protoreflect.EnumType.
//
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields.
func NewGoEnum(t *GoEnum) protoreflect.EnumType {
	if t.IsPlaceholder() {
		panic("enum descriptor must not be a placeholder")
	}
	if t.New == nil {
		panic("invalid nil constructor for enum kind")
	}
	return goEnum{t}
}
func (p goEnum) GoNew(n protoreflect.EnumNumber) protoreflect.ProtoEnum {
	e := p.New(p, n)
	p.once.Do(func() { p.goType = reflect.TypeOf(e) })
	if p.goType != reflect.TypeOf(e) {
		panic(fmt.Sprintf("mismatching types for enum: got %T, want %v", e, p.goType))
	}
	return e
}
func (p goEnum) GoType() reflect.Type {
	p.once.Do(func() { p.goType = reflect.TypeOf(p.New(p, 0)) })
	return p.goType
}

// GoMessage is a constructor for a protoreflect.MessageType.
type GoMessage struct {
	protoreflect.MessageDescriptor

	// New returns a new empty proto.Message instance.
	// The constructor must return the same concrete type for each invocation.
	New func(protoreflect.MessageType) protoreflect.ProtoMessage

	once   sync.Once
	goType reflect.Type
}
type goMessage struct{ *GoMessage }

// NewGoMessage creates a new protoreflect.MessageType.
//
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields.
func NewGoMessage(t *GoMessage) protoreflect.MessageType {
	if t.IsPlaceholder() {
		panic("message descriptor must not be a placeholder")
	}
	if t.New == nil {
		panic("invalid nil constructor for message kind")
	}
	return goMessage{t}
}
func (p goMessage) GoNew() protoreflect.ProtoMessage {
	m := p.New(p)
	p.once.Do(func() { p.goType = reflect.TypeOf(m) })
	if p.goType != reflect.TypeOf(m) {
		panic(fmt.Sprintf("mismatching types for message: got %T, want %v", m, p.goType))
	}
	return m
}
func (p goMessage) GoType() reflect.Type {
	p.once.Do(func() { p.goType = reflect.TypeOf(p.New(p)) })
	return p.goType
}

// GoExtension is a constructor for a protoreflect.ExtensionType.
type GoExtension struct {
	protoreflect.ExtensionDescriptor

	// NewEnum returns a concrete proto.Enum value with the given enum number.
	// The constructor must be provided if protoreflect.ExtensionDescriptor.Kind
	// is protoreflect.EnumKind.
	//
	// The returned enum must represent an protoreflect.EnumDescriptor
	// that matches protoreflect.ExtensionDescriptor.EnumType.
	NewEnum func(protoreflect.EnumNumber) protoreflect.ProtoEnum

	// NewMessage returns a new empty proto.Message instance.
	// The constructor must be provided if protoreflect.ExtensionDescriptor.Kind
	// is protoreflect.MessageKind or protoreflect.GroupKind.
	//
	// The returned message must represent an protoreflect.MessageDescriptor
	// that matches protoreflect.ExtensionDescriptor.MessageType.
	NewMessage func() protoreflect.ProtoMessage

	// TODO: Separate NewEnum and NewMessage constructors make it possible for
	// users to provide a constructor that returns a Go type does not match
	// the corresponding protobuf descriptor in ExtensionDescriptor.
	// Checking for correctness is hard since descriptors are not comparable.
	//
	// An alternative API is for ExtensionDescriptor.{EnumType,MessageType}
	// to document that it must implement protoreflect.{EnumType,MessageType}.

	once        sync.Once
	new         func() interface{}
	goType      reflect.Type
	valueOf     func(v interface{}) protoreflect.Value
	interfaceOf func(v protoreflect.Value) interface{}
}
type goExtension struct{ *GoExtension }

// NewGoExtension creates a new protoreflect.ExtensionType.
//
// The Go type is currently determined automatically (although custom Go types
// may be supported in the future). The type is T for scalars and
// *[]T for vectors. Maps are not valid in extension fields.
// The type T is determined as follows:
//
//	+------------+-------------------------------------+
//	| Go type    | Protobuf kind                       |
//	+------------+-------------------------------------+
//	| bool       | BoolKind                            |
//	| int32      | Int32Kind, Sint32Kind, Sfixed32Kind |
//	| int64      | Int64Kind, Sint64Kind, Sfixed64Kind |
//	| uint32     | Uint32Kind, Fixed32Kind             |
//	| uint64     | Uint64Kind, Fixed64Kind             |
//	| float32    | FloatKind                           |
//	| float64    | DoubleKind                          |
//	| string     | StringKind                          |
//	| []byte     | BytesKind                           |
//	| E          | EnumKind                            |
//	| M          | MessageKind, GroupKind              |
//	+------------+-------------------------------------+
//
// The type E is the concrete enum type returned by NewEnum,
// which is often, but not required to be, a named int32 type.
// The type M is the concrete message type returned by NewMessage,
// which is often, but not required to be, a pointer to a named struct type.
//
// The caller must relinquish full ownership of the input t and must not
// access or mutate any fields.
func NewGoExtension(t *GoExtension) protoreflect.ExtensionType {
	if t.ExtendedType() == nil {
		panic("field descriptor does not extend a message")
	}
	switch t.Kind() {
	case protoreflect.EnumKind:
		if t.NewEnum == nil {
			panic("enum constructor not provided for enum kind")
		}
		if t.NewMessage != nil {
			panic("message constructor provided for enum kind")
		}
	case protoreflect.MessageKind, protoreflect.GroupKind:
		if t.NewMessage == nil {
			panic("message constructor not provided for message kind")
		}
		if t.NewEnum != nil {
			panic("enum constructor provided for message kind")
		}
	default:
		if t.NewMessage != nil || t.NewEnum != nil {
			panic(fmt.Sprintf("enum or message constructor provided for %v kind", t.Kind()))
		}
	}
	return goExtension{t}
}
func (p goExtension) GoNew() interface{} {
	p.lazyInit()
	v := p.new()
	if reflect.TypeOf(v) != p.goType {
		panic(fmt.Sprintf("invalid type: got %T, want %v", v, p.goType))
	}
	return v
}
func (p goExtension) GoType() reflect.Type {
	p.lazyInit()
	return p.goType
}
func (p goExtension) ValueOf(v interface{}) protoreflect.Value {
	p.lazyInit()
	if reflect.TypeOf(v) != p.goType {
		panic(fmt.Sprintf("invalid type: got %T, want %v", v, p.goType))
	}
	return p.valueOf(v)
}
func (p goExtension) InterfaceOf(pv protoreflect.Value) interface{} {
	p.lazyInit()
	v := p.interfaceOf(pv)
	if reflect.TypeOf(v) != p.goType {
		panic(fmt.Sprintf("invalid type: got %T, want %v", v, p.goType))
	}
	return v
}
func (p goExtension) lazyInit() {
	p.once.Do(func() {
		switch p.Cardinality() {
		case protoreflect.Optional:
			switch p.Kind() {
			case protoreflect.EnumKind:
				p.goType = reflect.TypeOf(p.NewEnum(0))
				p.new = func() interface{} { return p.NewEnum(p.Default().Enum()) }
				p.valueOf = func(v interface{}) protoreflect.Value {
					ev := v.(protoreflect.ProtoEnum).ProtoReflect()
					return protoreflect.ValueOf(ev.Number())
				}
				p.interfaceOf = func(pv protoreflect.Value) interface{} {
					return p.NewEnum(pv.Enum())
				}
			case protoreflect.MessageKind, protoreflect.GroupKind:
				p.goType = reflect.TypeOf(p.NewMessage())
				p.new = func() interface{} { return p.NewMessage() }
				p.valueOf = func(v interface{}) protoreflect.Value {
					return protoreflect.ValueOf(v)
				}
				p.interfaceOf = func(pv protoreflect.Value) interface{} {
					return pv.Message().Interface()
				}
			default:
				p.goType = goTypeForPBKind[p.Kind()]
				p.new = func() interface{} { return p.Default().Interface() }
				p.valueOf = func(v interface{}) protoreflect.Value {
					return protoreflect.ValueOf(v)
				}
				p.interfaceOf = func(pv protoreflect.Value) interface{} {
					v := pv.Interface()
					return v
				}
			}
		case protoreflect.Repeated:
			var goType reflect.Type
			switch p.Kind() {
			case protoreflect.EnumKind:
				goType = reflect.TypeOf(p.NewEnum(p.Default().Enum()))
			case protoreflect.MessageKind, protoreflect.GroupKind:
				goType = reflect.TypeOf(p.NewMessage())
			default:
				goType = goTypeForPBKind[p.Kind()]
			}
			c := value.NewConverter(goType, p.Kind())
			p.goType = reflect.PtrTo(reflect.SliceOf(goType))
			p.new = func() interface{} { return reflect.New(p.goType.Elem()).Interface() }
			p.valueOf = func(v interface{}) protoreflect.Value {
				return protoreflect.ValueOf(value.VectorOf(v, c))
			}
			p.interfaceOf = func(v protoreflect.Value) interface{} {
				// TODO: Can we assume that Vector implementations know how
				// to unwrap themselves?
				// Should this be part of the public API in protoreflect?
				return v.Vector().(value.Unwrapper).Unwrap()
			}
		default:
			panic(fmt.Sprintf("invalid cardinality: %v", p.Cardinality()))
		}
	})
}

var goTypeForPBKind = map[protoreflect.Kind]reflect.Type{
	protoreflect.BoolKind:     reflect.TypeOf(bool(false)),
	protoreflect.Int32Kind:    reflect.TypeOf(int32(0)),
	protoreflect.Sint32Kind:   reflect.TypeOf(int32(0)),
	protoreflect.Fixed32Kind:  reflect.TypeOf(int32(0)),
	protoreflect.Int64Kind:    reflect.TypeOf(int64(0)),
	protoreflect.Sint64Kind:   reflect.TypeOf(int64(0)),
	protoreflect.Fixed64Kind:  reflect.TypeOf(int64(0)),
	protoreflect.Uint32Kind:   reflect.TypeOf(uint32(0)),
	protoreflect.Sfixed32Kind: reflect.TypeOf(uint32(0)),
	protoreflect.Uint64Kind:   reflect.TypeOf(uint64(0)),
	protoreflect.Sfixed64Kind: reflect.TypeOf(uint64(0)),
	protoreflect.FloatKind:    reflect.TypeOf(float32(0)),
	protoreflect.DoubleKind:   reflect.TypeOf(float64(0)),
	protoreflect.StringKind:   reflect.TypeOf(string("")),
	protoreflect.BytesKind:    reflect.TypeOf([]byte(nil)),
}
<|endoftext|>