<commit_before>package main

import (
	"bytes"
	"fmt"
	"strings"
)

const (
	HEADING_UNDERLINE_LENGTH = 20
	TABLE_LEFT_SPACING       = 5
)

func getRepeatedChars(character string, repeatCount int) string {
	formatted := ""
	for i := 0; i < repeatCount; i++ {
		formatted = fmt.Sprintf("%s%s", formatted, character)
	}
	return formatted
}

func formatSpecHeading(specHeading string) string {
	return formatHeading(specHeading, "=")
}

func formatScenarioHeading(scenarioHeading string) string {
	return fmt.Sprintf("%s", formatHeading(scenarioHeading, "-"))
}

func formatStep(step *step) string {
	text := step.value
	paramCount := strings.Count(text, PARAMETER_PLACEHOLDER)
	for i := 0; i < paramCount; i++ {
		argument := step.args[i]
		formattedArg := ""
		if argument.argType == tableArg || argument.argType == specialTable {
			formattedTable := formatTable(&argument.table)
			formattedArg = fmt.Sprintf("\n%s", formattedTable)
		} else if argument.argType == dynamic {
			formattedArg = fmt.Sprintf("<%s>", argument.value)
		} else {
			formattedArg = fmt.Sprintf("\"%s\"", argument.value)
		}
		text = strings.Replace(text, PARAMETER_PLACEHOLDER, formattedArg, 1)
	}
	stepText := ""
	if strings.HasSuffix(text, "\n") {
		stepText = fmt.Sprintf("* %s", text)
	} else {
		stepText = fmt.Sprintf("* %s\n", text)
	}
	return stepText
}

func formatConcept(protoConcept *ProtoConcept) string {
	conceptText := "# "
	for _, fragment := range protoConcept.ConceptStep.GetFragments() {
		if fragment.GetFragmentType() == Fragment_Text {
			conceptText = conceptText + fragment.GetText()
		} else if fragment.GetFragmentType() == Fragment_Parameter {
			if fragment.GetParameter().GetParameterType() == (Parameter_Table | Parameter_Special_Table) {
				conceptText += "\n" + formatTable(tableFrom(fragment.GetParameter().GetTable()))
			} else {
				conceptText = conceptText + "\"" + fragment.GetParameter().GetValue() + "\""
			}
		}
	}
	return conceptText
}

func formatHeading(heading, headingChar string) string {
	trimmedHeading := strings.TrimSpace(heading)
	length := len(trimmedHeading)
	if length > HEADING_UNDERLINE_LENGTH {
		length = HEADING_UNDERLINE_LENGTH
	}

	return fmt.Sprintf("%s\n%s\n", trimmedHeading, getRepeatedChars(headingChar, length))
}

func formatTable(table *table) string {
	columnToWidthMap := make(map[int]int)
	for i, header := range table.headers {
		//table.get(header) returns a list of cells in that particular column
		cells := table.get(header)
		columnToWidthMap[i] = findLongestCellWidth(cells, len(header))
	}

	var tableStringBuffer bytes.Buffer
	tableStringBuffer.WriteString(fmt.Sprintf("%s|", getRepeatedChars(" ", TABLE_LEFT_SPACING)))
	for i, header := range table.headers {
		width := columnToWidthMap[i]
		tableStringBuffer.WriteString(fmt.Sprintf("%s|", addPaddingToCell(header, width)))
	}

	tableStringBuffer.WriteString("\n")
	tableStringBuffer.WriteString(fmt.Sprintf("%s|", getRepeatedChars(" ", TABLE_LEFT_SPACING)))
	for i, _ := range table.headers {
		width := columnToWidthMap[i]
		cell := getRepeatedChars("-", width)
		tableStringBuffer.WriteString(fmt.Sprintf("%s|", addPaddingToCell(cell, width)))
	}

	tableStringBuffer.WriteString("\n")
	for _, row := range table.getRows() {
		tableStringBuffer.WriteString(fmt.Sprintf("%s|", getRepeatedChars(" ", TABLE_LEFT_SPACING)))
		for i, cell := range row {
			width := columnToWidthMap[i]
			tableStringBuffer.WriteString(fmt.Sprintf("%s|", addPaddingToCell(cell, width)))
		}
		tableStringBuffer.WriteString("\n")
	}

	return string(tableStringBuffer.Bytes())
}

func addPaddingToCell(cellValue string, width int) string {
	padding := getRepeatedChars(" ", width-len(cellValue))
	return fmt.Sprintf("%s%s", cellValue, padding)
}

func findLongestCellWidth(columnCells []tableCell, minValue int) int {
	longestLength := minValue
	for _, cellValue := range columnCells {
		cellValueLen := len(cellValue.value)
		if cellValueLen > longestLength {
			longestLength = cellValueLen
		}
	}
	return longestLength
}

func formatItem(item item) string {
	switch item.kind() {
	case commentKind:
		comment := item.(*comment)
		if comment.value == "\n" {
			return comment.value
		}
		return fmt.Sprintf("%s\n", comment.value)
	case stepKind:
		step := item.(*step)
		return formatStep(step)
	case tableKind:
		table := item.(*table)
		return formatTable(table)
	case scenarioKind:
		scenario := item.(*scenario)
		var b bytes.Buffer
		b.WriteString(formatScenarioHeading(scenario.heading.value))
		b.WriteString(formatItems(scenario.items))
		return string(b.Bytes())
	case tagKind:
		tags := item.(*tags)
		var b bytes.Buffer
		b.WriteString("tags: ")
		for i, tag := range tags.values {
			b.WriteString(tag)
			if (i + 1) != len(tags.values) {
				b.WriteString(", ")
			}
		}
		b.WriteString("\n")
		return string(b.Bytes())
	}
	return ""
}

func formatItems(items []item) string {
	var result bytes.Buffer
	for _, item := range items {
		result.WriteString(formatItem(item))
	}
	return string(result.Bytes())
}

func formatSpecification(specification *specification) string {
	var formattedText bytes.Buffer
	formattedText.WriteString(formatSpecHeading(specification.heading.value))
	formattedText.WriteString(formatItems(specification.items))
	return string(formattedText.Bytes())
}
<commit_msg>Changed the formatter of heading underline to heading length<commit_after>package main

import (
	"bytes"
	"fmt"
	"strings"
)

const (
	TABLE_LEFT_SPACING = 5
)

func getRepeatedChars(character string, repeatCount int) string {
	formatted := ""
	for i := 0; i < repeatCount; i++ {
		formatted = fmt.Sprintf("%s%s", formatted, character)
	}
	return formatted
}

func formatSpecHeading(specHeading string) string {
	return formatHeading(specHeading, "=")
}

func formatScenarioHeading(scenarioHeading string) string {
	return fmt.Sprintf("%s", formatHeading(scenarioHeading, "-"))
}

func formatStep(step *step) string {
	text := step.value
	paramCount := strings.Count(text, PARAMETER_PLACEHOLDER)
	for i := 0; i < paramCount; i++ {
		argument := step.args[i]
		formattedArg := ""
		if argument.argType == tableArg || argument.argType == specialTable {
			formattedTable := formatTable(&argument.table)
			formattedArg = fmt.Sprintf("\n%s", formattedTable)
		} else if argument.argType == dynamic {
			formattedArg = fmt.Sprintf("<%s>", argument.value)
		} else {
			formattedArg = fmt.Sprintf("\"%s\"", argument.value)
		}
		text = strings.Replace(text, PARAMETER_PLACEHOLDER, formattedArg, 1)
	}
	stepText := ""
	if strings.HasSuffix(text, "\n") {
		stepText = fmt.Sprintf("* %s", text)
	} else {
		stepText = fmt.Sprintf("* %s\n", text)
	}
	return stepText
}

func formatConcept(protoConcept *ProtoConcept) string {
	conceptText := "# "
	for _, fragment := range protoConcept.ConceptStep.GetFragments() {
		if fragment.GetFragmentType() == Fragment_Text {
			conceptText = conceptText + fragment.GetText()
		} else if fragment.GetFragmentType() == Fragment_Parameter {
			if fragment.GetParameter().GetParameterType() == (Parameter_Table | Parameter_Special_Table) {
				conceptText += "\n" + formatTable(tableFrom(fragment.GetParameter().GetTable()))
			} else {
				conceptText = conceptText + "\"" + fragment.GetParameter().GetValue() + "\""
			}
		}
	}
	return conceptText
}

func formatHeading(heading, headingChar string) string {
	trimmedHeading := strings.TrimSpace(heading)
	length := len(trimmedHeading)
	return fmt.Sprintf("%s\n%s\n", trimmedHeading, getRepeatedChars(headingChar, length))
}

func formatTable(table *table) string {
	columnToWidthMap := make(map[int]int)
	for i, header := range table.headers {
		//table.get(header) returns a list of cells in that particular column
		cells := table.get(header)
		columnToWidthMap[i] = findLongestCellWidth(cells, len(header))
	}

	var tableStringBuffer bytes.Buffer
	tableStringBuffer.WriteString(fmt.Sprintf("%s|", getRepeatedChars(" ", TABLE_LEFT_SPACING)))
	for i, header := range table.headers {
		width := columnToWidthMap[i]
		tableStringBuffer.WriteString(fmt.Sprintf("%s|", addPaddingToCell(header, width)))
	}

	tableStringBuffer.WriteString("\n")
	tableStringBuffer.WriteString(fmt.Sprintf("%s|", getRepeatedChars(" ", TABLE_LEFT_SPACING)))
	for i, _ := range table.headers {
		width := columnToWidthMap[i]
		cell := getRepeatedChars("-", width)
		tableStringBuffer.WriteString(fmt.Sprintf("%s|", addPaddingToCell(cell, width)))
	}

	tableStringBuffer.WriteString("\n")
	for _, row := range table.getRows() {
		tableStringBuffer.WriteString(fmt.Sprintf("%s|", getRepeatedChars(" ", TABLE_LEFT_SPACING)))
		for i, cell := range row {
			width := columnToWidthMap[i]
			tableStringBuffer.WriteString(fmt.Sprintf("%s|", addPaddingToCell(cell, width)))
		}
		tableStringBuffer.WriteString("\n")
	}

	return string(tableStringBuffer.Bytes())
}

func addPaddingToCell(cellValue string, width int) string {
	padding := getRepeatedChars(" ", width-len(cellValue))
	return fmt.Sprintf("%s%s", cellValue, padding)
}

func findLongestCellWidth(columnCells []tableCell, minValue int) int {
	longestLength := minValue
	for _, cellValue := range columnCells {
		cellValueLen := len(cellValue.value)
		if cellValueLen > longestLength {
			longestLength = cellValueLen
		}
	}
	return longestLength
}

func formatItem(item item) string {
	switch item.kind() {
	case commentKind:
		comment := item.(*comment)
		if comment.value == "\n" {
			return comment.value
		}
		return fmt.Sprintf("%s\n", comment.value)
	case stepKind:
		step := item.(*step)
		return formatStep(step)
	case tableKind:
		table := item.(*table)
		return formatTable(table)
	case scenarioKind:
		scenario := item.(*scenario)
		var b bytes.Buffer
		b.WriteString(formatScenarioHeading(scenario.heading.value))
		b.WriteString(formatItems(scenario.items))
		return string(b.Bytes())
	case tagKind:
		tags := item.(*tags)
		var b bytes.Buffer
		b.WriteString("tags: ")
		for i, tag := range tags.values {
			b.WriteString(tag)
			if (i + 1) != len(tags.values) {
				b.WriteString(", ")
			}
		}
		b.WriteString("\n")
		return string(b.Bytes())
	}
	return ""
}

func formatItems(items []item) string {
	var result bytes.Buffer
	for _, item := range items {
		result.WriteString(formatItem(item))
	}
	return string(result.Bytes())
}

func formatSpecification(specification *specification) string {
	var formattedText bytes.Buffer
	formattedText.WriteString(formatSpecHeading(specification.heading.value))
	formattedText.WriteString(formatItems(specification.items))
	return string(formattedText.Bytes())
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.238"

func main() {
	// XXX (reed): normalize
	level, err := logrus.ParseLevel(os.Getenv("LOG_LEVEL"))
	if err != nil {
		level = logrus.InfoLevel
	}
	logrus.SetLevel(level)

	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.205", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory; use k8s for kuberneted")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.StringVar(&conf.MgmtListen, "mgmt-listen", ":8081", "management port to run on")
	flag.IntVar(&conf.ShutdownTimeout, "shutdown-timeout", 0, "graceful shutdown timeout")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckHealthy, "hc-healthy", 1, "threshold of success checks to declare node healthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.StringVar(&conf.Namespace, "namespace", "", "kubernetes namespace to monitor")
	flag.StringVar(&conf.LabelSelector, "label-selector", "", "kubernetes label selector to monitor")
	flag.IntVar(&conf.TargetPort, "target-port", 8080, "kubernetes port to target on selected pods")

	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	db, err := lb.NewDB(conf) // Handles case where DBurl == "k8s"
	if err != nil {
		logrus.WithError(err).Fatal("error setting up database")
	}
	defer db.Close()

	g, err := lb.NewAllGrouper(conf, db)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	servers := make([]*http.Server, 0, 1)
	handler := lb.NewProxy(k, g, r, conf)

	// a separate mgmt listener is requested? then let's create a LB traffic only server
	if conf.Listen != conf.MgmtListen {
		servers = append(servers, &http.Server{Addr: conf.Listen, Handler: handler})
		handler = lb.NullHandler()
	}

	// add mgmt endpoints to the handler
	handler = g.Wrap(handler) // add/del/list endpoints
	handler = r.Wrap(handler) // stats / dash endpoint

	servers = append(servers, &http.Server{Addr: conf.MgmtListen, Handler: handler})
	serve(servers, &conf)
}

func serve(servers []*http.Server, conf *lb.Config) {

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)

	for i := 0; i < len(servers); i++ {
		go func(idx int) {
			err := servers[idx].ListenAndServe()
			if err != nil && err != http.ErrServerClosed {
				logrus.WithFields(logrus.Fields{"server_id": idx}).WithError(err).Fatal("server error")
			} else {
				logrus.WithFields(logrus.Fields{"server_id": idx}).Info("server stopped")
			}
		}(i)
	}

	sig := <-ch
	logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")

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

		ctx := context.Background()

		if conf.ShutdownTimeout > 0 {
			tmpCtx, cancel := context.WithTimeout(context.Background(), time.Duration(conf.ShutdownTimeout)*time.Second)
			ctx = tmpCtx
			defer cancel()
		}

		err := servers[i].Shutdown(ctx) // safe shutdown
		if err != nil {
			logrus.WithFields(logrus.Fields{"server_id": i}).WithError(err).Fatal("server shutdown error")
		} else {
			logrus.WithFields(logrus.Fields{"server_id": i}).Info("server shutdown")
		}
	}
}
<commit_msg>fnlb: 0.0.239 release [skip ci]<commit_after>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.239"

func main() {
	// XXX (reed): normalize
	level, err := logrus.ParseLevel(os.Getenv("LOG_LEVEL"))
	if err != nil {
		level = logrus.InfoLevel
	}
	logrus.SetLevel(level)

	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.206", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory; use k8s for kuberneted")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.StringVar(&conf.MgmtListen, "mgmt-listen", ":8081", "management port to run on")
	flag.IntVar(&conf.ShutdownTimeout, "shutdown-timeout", 0, "graceful shutdown timeout")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckHealthy, "hc-healthy", 1, "threshold of success checks to declare node healthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.StringVar(&conf.Namespace, "namespace", "", "kubernetes namespace to monitor")
	flag.StringVar(&conf.LabelSelector, "label-selector", "", "kubernetes label selector to monitor")
	flag.IntVar(&conf.TargetPort, "target-port", 8080, "kubernetes port to target on selected pods")

	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	db, err := lb.NewDB(conf) // Handles case where DBurl == "k8s"
	if err != nil {
		logrus.WithError(err).Fatal("error setting up database")
	}
	defer db.Close()

	g, err := lb.NewAllGrouper(conf, db)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	servers := make([]*http.Server, 0, 1)
	handler := lb.NewProxy(k, g, r, conf)

	// a separate mgmt listener is requested? then let's create a LB traffic only server
	if conf.Listen != conf.MgmtListen {
		servers = append(servers, &http.Server{Addr: conf.Listen, Handler: handler})
		handler = lb.NullHandler()
	}

	// add mgmt endpoints to the handler
	handler = g.Wrap(handler) // add/del/list endpoints
	handler = r.Wrap(handler) // stats / dash endpoint

	servers = append(servers, &http.Server{Addr: conf.MgmtListen, Handler: handler})
	serve(servers, &conf)
}

func serve(servers []*http.Server, conf *lb.Config) {

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)

	for i := 0; i < len(servers); i++ {
		go func(idx int) {
			err := servers[idx].ListenAndServe()
			if err != nil && err != http.ErrServerClosed {
				logrus.WithFields(logrus.Fields{"server_id": idx}).WithError(err).Fatal("server error")
			} else {
				logrus.WithFields(logrus.Fields{"server_id": idx}).Info("server stopped")
			}
		}(i)
	}

	sig := <-ch
	logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")

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

		ctx := context.Background()

		if conf.ShutdownTimeout > 0 {
			tmpCtx, cancel := context.WithTimeout(context.Background(), time.Duration(conf.ShutdownTimeout)*time.Second)
			ctx = tmpCtx
			defer cancel()
		}

		err := servers[i].Shutdown(ctx) // safe shutdown
		if err != nil {
			logrus.WithFields(logrus.Fields{"server_id": i}).WithError(err).Fatal("server shutdown error")
		} else {
			logrus.WithFields(logrus.Fields{"server_id": i}).Info("server shutdown")
		}
	}
}
<|endoftext|>


<commit_before>package fork

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"

	"testing"
)

var (
	TestForm       Form  = NewTestForm()
	TestListField  Field = ListField("listfield", makenewtextfield, TextField("zero"))
	TestFormsField Field = FormsField("formfield", 2, SimpleForm())
	//TestMulti2 []Field = []Field{TextField("four"), TextField("five"), TextField("six")}
	//TestMulti3 []Field = []Field{TextField("seven"), TextField("eight"), TextField("nine")}
)

func makenewtextfield(name string, req *http.Request) Field {
	t := TextField(name)
	t.Set(req)
	return t
}

func SimpleForm() Form {
	return &testform{
		fields: []Field{
			TextField("formfieldtext"),
			BooleanField("formfieldbool", "FFbool", false),
		},
	}
}

func NewTestForm() Form {
	return &testform{
		fields: []Field{
			//TextField("text"),
			//TextField("email"),
			//BooleanField("boolyes", "Yes", true),
			//BooleanField("boolno", "No", false),
			//HiddenField("invisible"),
			//TextAreaField("textarea", 10, 10),
			//TestListField,
			TestFormsField,
		},
	}
}

type testform struct {
	//Validater
	fields []Field
}

func (f *testform) New() Form {
	var newform testform = *f
	fs := f.fields
	newform.fields = nil
	for _, field := range fs {
		newform.fields = append(newform.fields, field.New())
	}
	return &newform
}

func (f *testform) Fields(fs ...Field) []Field {
	f.fields = append(f.fields, fs...)
	return f.fields
}

func (f *testform) Process(r *http.Request) {
	for _, fd := range f.Fields() {
		fd.Set(r)
	}
}

func (f *testform) Valid() bool {
	for _, fd := range f.Fields() {
		if !fd.Valid() {
			return false
		}
	}
	return true
}

func (f *testform) Render() string {
	b := new(bytes.Buffer)
	for _, fd := range f.Fields() {
		_, _ = b.WriteString(fd.Render(fd))
	}
	return b.String()
}

type TestHandler struct {
	handlers map[string]http.HandlerFunc
}

func (t TestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h := t.handlers[r.Method]
	if h != nil {
		h(w, r)
	}
}

func testserve() *TestHandler {
	return &TestHandler{handlers: make(map[string]http.HandlerFunc)}
}

func testformget(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	t := TestForm.New()
	out := bytes.NewBuffer([]byte(`<form action="/" method="POST">`))
	out.WriteString(t.Render())
	out.WriteString("</form>")
	w.Write(out.Bytes())
}

func testfieldrender(w http.ResponseWriter, r *http.Request) {
	//w.WriteHeader(200)
	//t := TestForm.New()
	//out := bytes.NewBuffer([]byte(`<form action="/" method="POST">`))
	//out.WriteString(t.Render())
	//out.WriteString("</form>")
	//w.Write(out.Bytes())
}

func testformpost(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	t := TestForm.New()
	t.Process(r)
	//for _, fd := range t.Fields() {
	//	fmt.Printf("%#v\n", fd.Get())
	//}
	out := bytes.NewBuffer([]byte(`<form action="/" method="POST">`))
	out.WriteString(t.Render())
	out.WriteString("</form>")
	w.Write(out.Bytes())
}

func TestGetForm(t *testing.T) {
	ts := testserve()
	ts.handlers["GET"] = testformget
	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()
	ts.ServeHTTP(w, req)
	fmt.Printf("%+v\n\n\n", w.Body)
}

func TestPostForm(t *testing.T) {
	ts := testserve()
	ts.handlers["POST"] = testformpost
	req, _ := http.NewRequest("POST", "/", nil)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	//v, _ := url.ParseQuery(`testtext=TESTTEXT1&email=test@test.com&testboolyes=false&testboolno=true&invisible=unseen&textareatest='4scoreN7yearsago'&listfield-0-zero='Iam0'&listfield-1-one='Iam1'&formfield-0-formfieldtext-0=textinaformfieldtextfield&formfield-0-formfieldbool-1=false`)
	v, _ := url.ParseQuery(`formfield-0-formfieldtext-0=FORMFIELD0textfield&formfield-0-formfieldbool-1=true&formfield-1-formfieldtext-0=formfield1text&formfield-1-formfieldbool-1=false`)
	//fmt.Printf("%+v\n", v)
	req.PostForm = v
	w := httptest.NewRecorder()
	ts.ServeHTTP(w, req)
	fmt.Printf("%+v\n\n\n", w.Body)
}
<commit_msg>listfields, formfields, refactor of bool fields<commit_after>package fork

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"

	"testing"
)

var (
	TestForm       Form  = NewTestForm()
	TestListField  Field = ListField("listfield", makenewtextfield, TextField("zero"))
	TestFormsField Field = FormsField("formfield", 2, SimpleForm())
	//TestMulti2 []Field = []Field{TextField("four"), TextField("five"), TextField("six")}
	//TestMulti3 []Field = []Field{TextField("seven"), TextField("eight"), TextField("nine")}
)

func makenewtextfield(name string, req *http.Request) Field {
	t := TextField(name)
	t.Set(req)
	return t
}

func SimpleForm() Form {
	return &testform{
		fields: []Field{
			TextField("formfieldtext"),
			BooleanField("formfieldbool", "FFbool", false),
		},
	}
}

func NewTestForm() Form {
	return &testform{
		fields: []Field{
			//TextField("text"),
			//TextField("email"),
			//BooleanField("boolyes", "Yes", true),
			//BooleanField("boolno", "No", false),
			//HiddenField("invisible"),
			//TextAreaField("textarea", 10, 10),
			//TestListField,
			TestFormsField,
		},
	}
}

type testform struct {
	//Validater
	fields []Field
}

func (f *testform) New() Form {
	var newform testform = *f
	fs := f.fields
	newform.fields = nil
	for _, field := range fs {
		newform.fields = append(newform.fields, field.New())
	}
	return &newform
}

func (f *testform) Fields(fs ...Field) []Field {
	f.fields = append(f.fields, fs...)
	return f.fields
}

func (f *testform) Process(r *http.Request) {
	for _, fd := range f.Fields() {
		fd.Set(r)
	}
}

func (f *testform) Valid() bool {
	for _, fd := range f.Fields() {
		if !fd.Valid() {
			return false
		}
	}
	return true
}

func (f *testform) Render() string {
	b := new(bytes.Buffer)
	for _, fd := range f.Fields() {
		_, _ = b.WriteString(fd.Render(fd))
	}
	return b.String()
}

type TestHandler struct {
	handlers map[string]http.HandlerFunc
}

func (t TestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h := t.handlers[r.Method]
	if h != nil {
		h(w, r)
	}
}

func testserve() *TestHandler {
	return &TestHandler{handlers: make(map[string]http.HandlerFunc)}
}

func testformget(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	t := TestForm.New()
	out := bytes.NewBuffer([]byte(`<form action="/" method="POST">`))
	out.WriteString(t.Render())
	out.WriteString("</form>")
	w.Write(out.Bytes())
}

func testfieldrender(w http.ResponseWriter, r *http.Request) {
	//w.WriteHeader(200)
	//t := TestForm.New()
	//out := bytes.NewBuffer([]byte(`<form action="/" method="POST">`))
	//out.WriteString(t.Render())
	//out.WriteString("</form>")
	//w.Write(out.Bytes())
}

func testformpost(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	t := TestForm.New()
	t.Process(r)
	//for _, fd := range t.Fields() {
	//	fmt.Printf("%#v\n", fd.Get())
	//}
	out := bytes.NewBuffer([]byte(`<form action="/" method="POST">`))
	out.WriteString(t.Render())
	out.WriteString("</form>")
	w.Write(out.Bytes())
}

func TestGetForm(t *testing.T) {
	ts := testserve()
	ts.handlers["GET"] = testformget
	req, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()
	ts.ServeHTTP(w, req)
	fmt.Printf("%+v\n\n\n", w.Body)
}

func TestPostForm(t *testing.T) {
	ts := testserve()
	ts.handlers["POST"] = testformpost
	req, _ := http.NewRequest("POST", "/", nil)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	v, _ := url.ParseQuery(`testtext=TESTTEXT1&email=test@test.com&testboolyes=false&testboolno=true&invisible=unseen&textareatest='4scoreN7yearsago'&listfield-0-zero='Iam0'&listfield-1-one='Iam1'&formfield-0-formfieldtext-0=textinaformfieldtextfield&formfield-0-formfieldbool-1=false`)
	//fmt.Printf("%+v\n", v)
	req.PostForm = v
	w := httptest.NewRecorder()
	ts.ServeHTTP(w, req)
	fmt.Printf("%+v\n\n\n", w.Body)
}

func TestPostListField(t *testing.T) {
	ts := testserve()
	ts.handlers["POST"] = testformpost
	req, _ := http.NewRequest("POST", "/", nil)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	v, _ := url.ParseQuery(`listfield-0-zero='Iam0'&listfield-1-one='Iam1'`)
	//fmt.Printf("%+v\n", v)
	req.PostForm = v
	w := httptest.NewRecorder()
	ts.ServeHTTP(w, req)
	fmt.Printf("%+v\n\n\n", w.Body)

}

func TestPostFormFields(t *testing.T) {
	ts := testserve()
	ts.handlers["POST"] = testformpost
	req, _ := http.NewRequest("POST", "/", nil)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	v, _ := url.ParseQuery(`formfield-0-formfieldtext-0=FORMFIELD0textfield&formfield-0-formfieldbool-1=true&formfield-1-formfieldtext-0=formfield1text&formfield-1-formfieldbool-1=false`)
	//fmt.Printf("%+v\n", v)
	req.PostForm = v
	w := httptest.NewRecorder()
	ts.ServeHTTP(w, req)
	fmt.Printf("%+v\n\n\n", w.Body)
}
<|endoftext|>


<commit_before>// A command line tool for fragmenta which can be used to build and run websites
// this tool calls subcommands for most of the work, usually one command per file in this pkg
// See docs at http://godoc.org/github.com/fragmenta/fragmenta

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
)

const (
	// The version of this tool
	fragmentaVersion = "1.2.1"

	// Used for outputting console messages
	fragmentaDivider = "\n------\n"
)

var (
	// ConfigDevelopment holds the development config from fragmenta.json
	ConfigDevelopment map[string]string

	// ConfigProduction holds development config from fragmenta.json
	ConfigProduction map[string]string

	// ConfigTest holds the app test config from fragmenta.json
	ConfigTest map[string]string
)

// main - Parse the command line arguments and respond
func main() {

	log.SetFlags(log.Ltime)

	args := os.Args
	command := ""

	if len(args) > 1 {
		command = args[1]
	}

	// We should intelligently read project path depending on the command?
	// Or just assume we act on the current directory?
	// NB projectPath might be different from the path in config, which MUST be within a GOPATH
	// this is the local project path
	projectPath, err := filepath.Abs(".")
	if err != nil {
		log.Printf("Error getting path %s", err)
		return
	}
	if isValidProject(projectPath) {
		readConfig(projectPath)
	}

	switch command {

	case "new", "n":
		RunNew(args)

	case "version", "v":
		ShowVersion()

	case "help", "h", "wat", "?":
		ShowHelp(args)

	case "server", "s":
		if requireValidProject(projectPath) {
			RunServer(projectPath)
		}

	case "test", "t":
		if requireValidProject(projectPath) {
			RunTests(args)
		}

	case "build", "B":
		if requireValidProject(projectPath) {
			RunBuild(args)
		}

	case "generate", "g":
		if requireValidProject(projectPath) {
			RunGenerate(args)
		}

	case "migrate", "m":
		if requireValidProject(projectPath) {
			RunMigrate(args)
		}

	case "backup", "b":
		if requireValidProject(projectPath) {
			RunBackup(args)
		}

	case "restore", "r":
		if requireValidProject(projectPath) {
			RunRestore(args)
		}

	case "deploy", "d":
		if requireValidProject(projectPath) {
			RunDeploy(args)
		}

	default:
		if requireValidProject(projectPath) {
			RunServer(projectPath)
		} else {
			ShowHelp(args)
		}
	}

}

// ShowVersion shows the version of this tool
func ShowVersion() {
	helpString := fragmentaDivider
	helpString += fmt.Sprintf("Fragmenta version: %s", fragmentaVersion)
	helpString += fragmentaDivider
	log.Print(helpString)
}

// ShowHelp shows the help for this tool
func ShowHelp(args []string) {
	helpString := fragmentaDivider
	helpString += fmt.Sprintf("Fragmenta version: %s", fragmentaVersion)
	helpString += "\n  fragmenta version -> display version"
	helpString += "\n  fragmenta help -> display help"
	helpString += "\n  fragmenta new [app|cms|blog|URL] path/to/app -> creates a new app from the repository at URL at the path supplied"
	helpString += "\n  fragmenta -> builds and runs a fragmenta app"
	helpString += "\n  fragmenta server -> builds and runs a fragmenta app"
	helpString += "\n  fragmenta test  -> run tests"
	helpString += "\n  fragmenta migrate -> runs new sql migrations in db/migrate"
	helpString += "\n  fragmenta backup [development|production|test] -> backup the database to db/backup"
	helpString += "\n  fragmenta restore [development|production|test] -> backup the database from latest file in db/backup"
	helpString += "\n  fragmenta deploy [development|production|test] -> build and deploy using bin/deploy"
	helpString += "\n  fragmenta generate resource [name] [fieldname]:[fieldtype]* -> creates resource CRUD actions and views"
	helpString += "\n  fragmenta generate migration [name] -> creates a new named sql migration in db/migrate"

	helpString += fragmentaDivider
	log.Print(helpString)
}

// FIXME - move all instances of hardcoded paths out into optional app config variables
// Ideally we don't care about project structure apart from the load the fragmenta.json file

// serverName returns the name of our server file - TODO:read from config
func serverName() string {
	return "fragmenta-server" // for now, should use configs
}

func localServerPath(projectPath string) string {
	return fmt.Sprintf("%s/bin/%s-local", projectPath, serverName())
}

func serverPath(projectPath string) string {
	return fmt.Sprintf("%s/bin/%s", projectPath, serverName())
}

func serverCompilePath(projectPath string) string {
	return path.Join(projectPath, "server.go")
}

// Return the src to scan assets for compilation
// Can this be set within the project setup instead to avoid hardcoding here?
func srcPath(projectPath string) string {
	return projectPath + "src"
}

func publicPath(projectPath string) string {
	return projectPath + "public"
}

func configPath(projectPath string) string {
	return projectPath + "/secrets/fragmenta.json"
}

func templatesPath() string {
	return os.ExpandEnv("$GOPATH/src/github.com/fragmenta/fragmenta/templates")
}

// RunServer runs the server
func RunServer(projectPath string) {
	ShowVersion()

	killServer()

	log.Println("Building server...")
	err := buildServer(localServerPath(projectPath), nil)

	if err != nil {
		log.Printf("Error building server: %s", err)
		return
	}

	log.Println("Launching server...")
	cmd := exec.Command(localServerPath(projectPath))
	stdout, err := cmd.StdoutPipe()
	stderr, err := cmd.StderrPipe()
	err = cmd.Start()
	if err != nil {
		log.Println(err)
	}
	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	cmd.Wait()

}

// killServer kills the server with a unix command - FIXME:Windows
func killServer() {
	runCommand("killall", "-9", serverName())
}

// runCommand runs a command with exec.Command
func runCommand(command string, args ...string) ([]byte, error) {

	cmd := exec.Command(command, args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return output, err
	}

	return output, nil
}

// requireValidProject returns true if we have a valid project at projectPath
func requireValidProject(projectPath string) bool {
	if isValidProject(projectPath) {
		return true
	}

	log.Printf("\nNo fragmenta project found at this path\n")
	return false

}

// isValidProject returns true if this is a valid fragmenta project (checks for server.go file and config file)
func isValidProject(projectPath string) bool {

	// Make sure we have server.go at root of this dir
	_, err := os.Stat(serverCompilePath(projectPath))
	if err != nil {
		return false
	}

	// Make sure we have a config file under secrets
	_, err = os.Stat(configPath(projectPath))
	if err != nil {
		return false
	}

	return true
}

// fileExists returns true if this file exists
func fileExists(p string) bool {
	_, err := os.Stat(p)
	if err != nil && os.IsNotExist(err) {
		return false
	}

	return true
}

// readConfig reads our config file and set up the server accordingly
func readConfig(projectPath string) error {
	configPath := configPath(projectPath)

	// Read the config json file
	file, err := ioutil.ReadFile(configPath)
	if err != nil {
		log.Printf("Error opening config %s %v", configPath, err)
		return err
	}

	var data map[string]map[string]string
	err = json.Unmarshal(file, &data)
	if err != nil {
		log.Printf("Error parsing config %s %v", configPath, err)
		return err
	}

	ConfigDevelopment = data["development"]
	ConfigProduction = data["production"]
	ConfigTest = data["test"]

	return nil
}
<commit_msg>Bumped version to 1.3<commit_after>// A command line tool for fragmenta which can be used to build and run websites
// this tool calls subcommands for most of the work, usually one command per file in this pkg
// See docs at http://godoc.org/github.com/fragmenta/fragmenta

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
)

const (
	// The version of this tool
	fragmentaVersion = "1.3"

	// Used for outputting console messages
	fragmentaDivider = "\n------\n"
)

var (
	// ConfigDevelopment holds the development config from fragmenta.json
	ConfigDevelopment map[string]string

	// ConfigProduction holds development config from fragmenta.json
	ConfigProduction map[string]string

	// ConfigTest holds the app test config from fragmenta.json
	ConfigTest map[string]string
)

// main - Parse the command line arguments and respond
func main() {

	log.SetFlags(log.Ltime)

	args := os.Args
	command := ""

	if len(args) > 1 {
		command = args[1]
	}

	// We should intelligently read project path depending on the command?
	// Or just assume we act on the current directory?
	// NB projectPath might be different from the path in config, which MUST be within a GOPATH
	// this is the local project path
	projectPath, err := filepath.Abs(".")
	if err != nil {
		log.Printf("Error getting path %s", err)
		return
	}
	if isValidProject(projectPath) {
		readConfig(projectPath)
	}

	switch command {

	case "new", "n":
		RunNew(args)

	case "version", "v":
		ShowVersion()

	case "help", "h", "wat", "?":
		ShowHelp(args)

	case "server", "s":
		if requireValidProject(projectPath) {
			RunServer(projectPath)
		}

	case "test", "t":
		if requireValidProject(projectPath) {
			RunTests(args)
		}

	case "build", "B":
		if requireValidProject(projectPath) {
			RunBuild(args)
		}

	case "generate", "g":
		if requireValidProject(projectPath) {
			RunGenerate(args)
		}

	case "migrate", "m":
		if requireValidProject(projectPath) {
			RunMigrate(args)
		}

	case "backup", "b":
		if requireValidProject(projectPath) {
			RunBackup(args)
		}

	case "restore", "r":
		if requireValidProject(projectPath) {
			RunRestore(args)
		}

	case "deploy", "d":
		if requireValidProject(projectPath) {
			RunDeploy(args)
		}

	default:
		if requireValidProject(projectPath) {
			RunServer(projectPath)
		} else {
			ShowHelp(args)
		}
	}

}

// ShowVersion shows the version of this tool
func ShowVersion() {
	helpString := fragmentaDivider
	helpString += fmt.Sprintf("Fragmenta version: %s", fragmentaVersion)
	helpString += fragmentaDivider
	log.Print(helpString)
}

// ShowHelp shows the help for this tool
func ShowHelp(args []string) {
	helpString := fragmentaDivider
	helpString += fmt.Sprintf("Fragmenta version: %s", fragmentaVersion)
	helpString += "\n  fragmenta version -> display version"
	helpString += "\n  fragmenta help -> display help"
	helpString += "\n  fragmenta new [app|cms|blog|URL] path/to/app -> creates a new app from the repository at URL at the path supplied"
	helpString += "\n  fragmenta -> builds and runs a fragmenta app"
	helpString += "\n  fragmenta server -> builds and runs a fragmenta app"
	helpString += "\n  fragmenta test  -> run tests"
	helpString += "\n  fragmenta migrate -> runs new sql migrations in db/migrate"
	helpString += "\n  fragmenta backup [development|production|test] -> backup the database to db/backup"
	helpString += "\n  fragmenta restore [development|production|test] -> backup the database from latest file in db/backup"
	helpString += "\n  fragmenta deploy [development|production|test] -> build and deploy using bin/deploy"
	helpString += "\n  fragmenta generate resource [name] [fieldname]:[fieldtype]* -> creates resource CRUD actions and views"
	helpString += "\n  fragmenta generate migration [name] -> creates a new named sql migration in db/migrate"

	helpString += fragmentaDivider
	log.Print(helpString)
}

// FIXME - move all instances of hardcoded paths out into optional app config variables
// Ideally we don't care about project structure apart from the load the fragmenta.json file

// serverName returns the name of our server file - TODO:read from config
func serverName() string {
	return "fragmenta-server" // for now, should use configs
}

func localServerPath(projectPath string) string {
	return fmt.Sprintf("%s/bin/%s-local", projectPath, serverName())
}

func serverPath(projectPath string) string {
	return fmt.Sprintf("%s/bin/%s", projectPath, serverName())
}

func serverCompilePath(projectPath string) string {
	return path.Join(projectPath, "server.go")
}

// Return the src to scan assets for compilation
// Can this be set within the project setup instead to avoid hardcoding here?
func srcPath(projectPath string) string {
	return projectPath + "src"
}

func publicPath(projectPath string) string {
	return projectPath + "public"
}

func configPath(projectPath string) string {
	return projectPath + "/secrets/fragmenta.json"
}

func templatesPath() string {
	return os.ExpandEnv("$GOPATH/src/github.com/fragmenta/fragmenta/templates")
}

// RunServer runs the server
func RunServer(projectPath string) {
	ShowVersion()

	killServer()

	log.Println("Building server...")
	err := buildServer(localServerPath(projectPath), nil)

	if err != nil {
		log.Printf("Error building server: %s", err)
		return
	}

	log.Println("Launching server...")
	cmd := exec.Command(localServerPath(projectPath))
	stdout, err := cmd.StdoutPipe()
	stderr, err := cmd.StderrPipe()
	err = cmd.Start()
	if err != nil {
		log.Println(err)
	}
	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stderr, stderr)
	cmd.Wait()

}

// killServer kills the server with a unix command - FIXME:Windows
func killServer() {
	runCommand("killall", "-9", serverName())
}

// runCommand runs a command with exec.Command
func runCommand(command string, args ...string) ([]byte, error) {

	cmd := exec.Command(command, args...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return output, err
	}

	return output, nil
}

// requireValidProject returns true if we have a valid project at projectPath
func requireValidProject(projectPath string) bool {
	if isValidProject(projectPath) {
		return true
	}

	log.Printf("\nNo fragmenta project found at this path\n")
	return false

}

// isValidProject returns true if this is a valid fragmenta project (checks for server.go file and config file)
func isValidProject(projectPath string) bool {

	// Make sure we have server.go at root of this dir
	_, err := os.Stat(serverCompilePath(projectPath))
	if err != nil {
		return false
	}

	// Make sure we have a config file under secrets
	_, err = os.Stat(configPath(projectPath))
	if err != nil {
		return false
	}

	return true
}

// fileExists returns true if this file exists
func fileExists(p string) bool {
	_, err := os.Stat(p)
	if err != nil && os.IsNotExist(err) {
		return false
	}

	return true
}

// readConfig reads our config file and set up the server accordingly
func readConfig(projectPath string) error {
	configPath := configPath(projectPath)

	// Read the config json file
	file, err := ioutil.ReadFile(configPath)
	if err != nil {
		log.Printf("Error opening config %s %v", configPath, err)
		return err
	}

	var data map[string]map[string]string
	err = json.Unmarshal(file, &data)
	if err != nil {
		log.Printf("Error parsing config %s %v", configPath, err)
		return err
	}

	ConfigDevelopment = data["development"]
	ConfigProduction = data["production"]
	ConfigTest = data["test"]

	return nil
}
<|endoftext|>


<commit_before>package curator

import (
	"fmt"
	"sync/atomic"
	"time"

	"github.com/golang/glog"
)

type CuratorFrameworkState int32

const (
	LATENT  CuratorFrameworkState = iota // CuratorFramework.Start() has not yet been called
	STARTED                              // CuratorFramework.Start() has been called
	STOPPED                              // CuratorFramework.Close() has been called
)

func (s *CuratorFrameworkState) Change(oldState, newState CuratorFrameworkState) bool {
	return atomic.CompareAndSwapInt32((*int32)(s), int32(oldState), int32(newState))
}

func (s *CuratorFrameworkState) Value() CuratorFrameworkState {
	return CuratorFrameworkState(atomic.LoadInt32((*int32)(s)))
}

func (s CuratorFrameworkState) Check(state CuratorFrameworkState, msg string) {
	if s != state {
		panic(msg)
	}
}

const (
	DEFAULT_SESSION_TIMEOUT    time.Duration = 60 * time.Second
	DEFAULT_CONNECTION_TIMEOUT               = 15 * time.Second
)

// Zookeeper framework-style client
type CuratorFramework interface {
	// Start the client.
	// Most mutator methods will not work until the client is started
	Start() error

	// Stop the client
	Close() error

	// Returns the state of this instance
	State() CuratorFrameworkState

	// Return true if the client is started, not closed, etc.
	Started() bool

	// Start a create builder
	Create() CreateBuilder

	// Start a delete builder
	Delete() DeleteBuilder

	// Start an exists builder
	CheckExists() CheckExistsBuilder

	// Start a get data builder
	GetData() GetDataBuilder

	// Start a set data builder
	SetData() SetDataBuilder

	// Start a get children builder
	GetChildren() GetChildrenBuilder

	// Start a get ACL builder
	GetACL() GetACLBuilder

	// Start a set ACL builder
	SetACL() SetACLBuilder

	// Start a transaction builder
	InTransaction() CuratorTransaction

	// Returns the listenable interface for the Connect State
	ConnectionStateListenable() ConnectionStateListenable

	// Returns the listenable interface for events
	CuratorListenable() CuratorListenable

	// Returns the listenable interface for unhandled errors
	UnhandledErrorListenable() UnhandledErrorListenable

	// Return the managed zookeeper client
	ZookeeperClient() *CuratorZookeeperClient
}

func NewClient(connString string, retryPolicy RetryPolicy) CuratorFramework {
	return NewClientTimeout(connString, DEFAULT_SESSION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, retryPolicy)
}

func NewClientTimeout(connString string, sessionTimeout, connectionTimeout time.Duration, retryPolicy RetryPolicy) CuratorFramework {
	return Builder().ConnectString(connString).SessionTimeout(sessionTimeout).ConnectionTimeout(connectionTimeout).RetryPolicy(retryPolicy).Build()
}

type CuratorFrameworkBuilder interface {
	// Apply the current values and build a new CuratorFramework
	Build() CuratorFramework

	// Add connection authorization
	Authorization(scheme string, auth []byte) CuratorFrameworkBuilder

	// Add connection authorization.
	Authorizations(authInfos ...AuthInfo) CuratorFrameworkBuilder

	// Set the list of servers to connect to.
	ConnectString(connectString string) CuratorFrameworkBuilder

	// Set the list ensemble provider.
	EnsembleProvider(ensembleProvider EnsembleProvider) CuratorFrameworkBuilder

	// Sets the data to use when PathAndBytesable.ForPath(String) is used.
	DefaultData(defaultData []byte) CuratorFrameworkBuilder

	// As ZooKeeper is a shared space, users of a given cluster should stay within a pre-defined namespace.
	// If a namespace is set here, all paths will get pre-pended with the namespace
	Namespace(namespace string) CuratorFrameworkBuilder

	// session timeout
	SessionTimeout(sessionTimeout time.Duration) CuratorFrameworkBuilder

	// connection timeout
	ConnectionTimeout(connectionTimeout time.Duration) CuratorFrameworkBuilder

	// time to wait during close to join background threads
	MaxCloseWait(maxCloseWait time.Duration) CuratorFrameworkBuilder

	// retry policy to use
	RetryPolicy(retryPolicy RetryPolicy) CuratorFrameworkBuilder

	// the compression provider
	CompressionProvider(compressionProvider CompressionProvider) CuratorFrameworkBuilder

	// a provider for ACLs
	ACLProvider(aclProvider ACLProvider) CuratorFrameworkBuilder

	// allow ZooKeeper client to enter read only mode in case of a network partition
	CanBeReadOnly(canBeReadOnly bool) CuratorFrameworkBuilder
}

func Builder() CuratorFrameworkBuilder {
	return &curatorFrameworkBuilder{}
}

type curatorFrameworkBuilder struct {
	authInfos           []AuthInfo
	ensembleProvider    EnsembleProvider
	defaultData         []byte
	namespace           string
	sessionTimeout      time.Duration
	connectionTimeout   time.Duration
	maxCloseWait        time.Duration
	retryPolicy         RetryPolicy
	compressionProvider CompressionProvider
	aclProvider         ACLProvider
	canBeReadOnly       bool
}

func (b *curatorFrameworkBuilder) Build() CuratorFramework {
	return newCuratorFramework(b)
}

func (b *curatorFrameworkBuilder) Authorization(scheme string, auth []byte) CuratorFrameworkBuilder {
	return b.Authorizations(&authInfo{scheme, auth})
}

func (b *curatorFrameworkBuilder) Authorizations(authInfos ...AuthInfo) CuratorFrameworkBuilder {
	b.authInfos = append(b.authInfos, authInfos...)

	return b
}

func (b *curatorFrameworkBuilder) ConnectString(connectString string) CuratorFrameworkBuilder {
	b.ensembleProvider = &fixedEnsembleProvider{connectString}

	return b
}

func (b *curatorFrameworkBuilder) EnsembleProvider(ensembleProvider EnsembleProvider) CuratorFrameworkBuilder {
	b.ensembleProvider = ensembleProvider

	return b
}

func (b *curatorFrameworkBuilder) DefaultData(defaultData []byte) CuratorFrameworkBuilder {
	b.defaultData = defaultData

	return b
}

func (b *curatorFrameworkBuilder) Namespace(namespace string) CuratorFrameworkBuilder {
	b.namespace = namespace

	return b
}

func (b *curatorFrameworkBuilder) SessionTimeout(sessionTimeout time.Duration) CuratorFrameworkBuilder {
	b.sessionTimeout = sessionTimeout

	return b
}

func (b *curatorFrameworkBuilder) ConnectionTimeout(connectionTimeout time.Duration) CuratorFrameworkBuilder {
	b.connectionTimeout = connectionTimeout

	return b
}

func (b *curatorFrameworkBuilder) MaxCloseWait(maxCloseWait time.Duration) CuratorFrameworkBuilder {
	b.maxCloseWait = maxCloseWait

	return b
}

func (b *curatorFrameworkBuilder) RetryPolicy(retryPolicy RetryPolicy) CuratorFrameworkBuilder {
	b.retryPolicy = retryPolicy

	return b
}

func (b *curatorFrameworkBuilder) CompressionProvider(compressionProvider CompressionProvider) CuratorFrameworkBuilder {
	b.compressionProvider = compressionProvider

	return b
}

func (b *curatorFrameworkBuilder) ACLProvider(aclProvider ACLProvider) CuratorFrameworkBuilder {
	b.aclProvider = aclProvider

	return b
}

func (b *curatorFrameworkBuilder) CanBeReadOnly(canBeReadOnly bool) CuratorFrameworkBuilder {
	b.canBeReadOnly = canBeReadOnly

	return b
}

type curatorFramework struct {
	client                  *CuratorZookeeperClient
	stateManager            *ConnectionStateManager
	state                   CuratorFrameworkState
	listeners               CuratorListenable
	unhandledErrorListeners UnhandledErrorListenable
	defaultData             []byte
	retryPolicy             RetryPolicy
	compressionProvider     CompressionProvider
	aclProvider             ACLProvider
}

func newCuratorFramework(b *curatorFrameworkBuilder) *curatorFramework {
	c := &curatorFramework{
		client:                  NewCuratorZookeeperClient(b.ensembleProvider, b.sessionTimeout, b.connectionTimeout, b.retryPolicy, b.canBeReadOnly),
		listeners:               NewCuratorListenerContainer(),
		unhandledErrorListeners: NewUnhandledErrorListenerContainer(),
		defaultData:             b.defaultData,
		retryPolicy:             b.retryPolicy,
		compressionProvider:     b.compressionProvider,
		aclProvider:             b.aclProvider,
	}

	c.stateManager = NewConnectionStateManager(c)

	return c
}

func (c *curatorFramework) Start() error {
	if !c.state.Change(LATENT, STARTED) {
		return fmt.Errorf("Cannot be started more than once")
	} else if err := c.stateManager.Start(); err != nil {
		return fmt.Errorf("fail to start state manager, %s", err)
	} else if err := c.client.Start(); err != nil {
		return fmt.Errorf("fail to start client, %s", err)
	}

	return nil
}

func (c *curatorFramework) Close() error {
	if !c.state.Change(STARTED, STOPPED) {
		return nil
	}

	evt := &curatorEvent{eventType: CLOSING}

	c.listeners.ForEach(func(listener CuratorListener) error {
		return listener.EventReceived(c, evt)
	})

	c.listeners.Clear()
	c.unhandledErrorListeners.Clear()

	if err := c.stateManager.Close(); err != nil {
		glog.Errorf("fail to close state manager, %s", err)
	}

	return c.client.Close()
}

func (c *curatorFramework) State() CuratorFrameworkState {
	return c.state.Value()
}

func (c *curatorFramework) Started() bool {
	return c.State() == STARTED
}

func (c *curatorFramework) Create() CreateBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return &createBuilder{client: c}
}

func (c *curatorFramework) Delete() DeleteBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return &checkExistsBuilder{client: c}
}

func (c *curatorFramework) CheckExists() CheckExistsBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) GetData() GetDataBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) SetData() SetDataBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) GetChildren() GetChildrenBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) GetACL() GetACLBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) SetACL() SetACLBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) InTransaction() CuratorTransaction {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) ConnectionStateListenable() ConnectionStateListenable {
	return c.stateManager.listeners
}

func (c *curatorFramework) CuratorListenable() CuratorListenable {
	return c.listeners
}

func (c *curatorFramework) UnhandledErrorListenable() UnhandledErrorListenable {
	return c.unhandledErrorListeners
}

func (c *curatorFramework) ZookeeperClient() *CuratorZookeeperClient {
	return c.client
}

func (c *curatorFramework) fixForNamespace(path string, isSequential bool) string {
	return path
}

func (c *curatorFramework) unfixForNamespace(path string) string {
	return path
}

func (c *curatorFramework) getNamespaceWatcher(watcher Watcher) Watcher {
	return watcher
}
<commit_msg>handle events for listeners<commit_after>package curator

import (
	"fmt"
	"sync/atomic"
	"time"

	"github.com/golang/glog"
	"github.com/samuel/go-zookeeper/zk"
)

type CuratorFrameworkState int32

const (
	LATENT  CuratorFrameworkState = iota // CuratorFramework.Start() has not yet been called
	STARTED                              // CuratorFramework.Start() has been called
	STOPPED                              // CuratorFramework.Close() has been called
)

func (s *CuratorFrameworkState) Change(oldState, newState CuratorFrameworkState) bool {
	return atomic.CompareAndSwapInt32((*int32)(s), int32(oldState), int32(newState))
}

func (s *CuratorFrameworkState) Value() CuratorFrameworkState {
	return CuratorFrameworkState(atomic.LoadInt32((*int32)(s)))
}

func (s CuratorFrameworkState) Check(state CuratorFrameworkState, msg string) {
	if s != state {
		panic(msg)
	}
}

const (
	DEFAULT_SESSION_TIMEOUT    time.Duration = 60 * time.Second
	DEFAULT_CONNECTION_TIMEOUT               = 15 * time.Second
)

// Zookeeper framework-style client
type CuratorFramework interface {
	// Start the client.
	// Most mutator methods will not work until the client is started
	Start() error

	// Stop the client
	Close() error

	// Returns the state of this instance
	State() CuratorFrameworkState

	// Return true if the client is started, not closed, etc.
	Started() bool

	// Start a create builder
	Create() CreateBuilder

	// Start a delete builder
	Delete() DeleteBuilder

	// Start an exists builder
	CheckExists() CheckExistsBuilder

	// Start a get data builder
	GetData() GetDataBuilder

	// Start a set data builder
	SetData() SetDataBuilder

	// Start a get children builder
	GetChildren() GetChildrenBuilder

	// Start a get ACL builder
	GetACL() GetACLBuilder

	// Start a set ACL builder
	SetACL() SetACLBuilder

	// Start a transaction builder
	InTransaction() CuratorTransaction

	// Returns the listenable interface for the Connect State
	ConnectionStateListenable() ConnectionStateListenable

	// Returns the listenable interface for events
	CuratorListenable() CuratorListenable

	// Returns the listenable interface for unhandled errors
	UnhandledErrorListenable() UnhandledErrorListenable

	// Return the managed zookeeper client
	ZookeeperClient() *CuratorZookeeperClient
}

func NewClient(connString string, retryPolicy RetryPolicy) CuratorFramework {
	return NewClientTimeout(connString, DEFAULT_SESSION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT, retryPolicy)
}

func NewClientTimeout(connString string, sessionTimeout, connectionTimeout time.Duration, retryPolicy RetryPolicy) CuratorFramework {
	return Builder().ConnectString(connString).SessionTimeout(sessionTimeout).ConnectionTimeout(connectionTimeout).RetryPolicy(retryPolicy).Build()
}

type CuratorFrameworkBuilder interface {
	// Apply the current values and build a new CuratorFramework
	Build() CuratorFramework

	// Add connection authorization
	Authorization(scheme string, auth []byte) CuratorFrameworkBuilder

	// Add connection authorization.
	Authorizations(authInfos ...AuthInfo) CuratorFrameworkBuilder

	// Set the list of servers to connect to.
	ConnectString(connectString string) CuratorFrameworkBuilder

	// Set the list ensemble provider.
	EnsembleProvider(ensembleProvider EnsembleProvider) CuratorFrameworkBuilder

	// Sets the data to use when PathAndBytesable.ForPath(String) is used.
	DefaultData(defaultData []byte) CuratorFrameworkBuilder

	// As ZooKeeper is a shared space, users of a given cluster should stay within a pre-defined namespace.
	// If a namespace is set here, all paths will get pre-pended with the namespace
	Namespace(namespace string) CuratorFrameworkBuilder

	// session timeout
	SessionTimeout(sessionTimeout time.Duration) CuratorFrameworkBuilder

	// connection timeout
	ConnectionTimeout(connectionTimeout time.Duration) CuratorFrameworkBuilder

	// time to wait during close to join background threads
	MaxCloseWait(maxCloseWait time.Duration) CuratorFrameworkBuilder

	// retry policy to use
	RetryPolicy(retryPolicy RetryPolicy) CuratorFrameworkBuilder

	// the compression provider
	CompressionProvider(compressionProvider CompressionProvider) CuratorFrameworkBuilder

	// a provider for ACLs
	ACLProvider(aclProvider ACLProvider) CuratorFrameworkBuilder

	// allow ZooKeeper client to enter read only mode in case of a network partition
	CanBeReadOnly(canBeReadOnly bool) CuratorFrameworkBuilder
}

func Builder() CuratorFrameworkBuilder {
	return &curatorFrameworkBuilder{}
}

type curatorFrameworkBuilder struct {
	authInfos           []AuthInfo
	zookeeperDialer     ZookeeperDialer
	ensembleProvider    EnsembleProvider
	defaultData         []byte
	namespace           string
	sessionTimeout      time.Duration
	connectionTimeout   time.Duration
	maxCloseWait        time.Duration
	retryPolicy         RetryPolicy
	compressionProvider CompressionProvider
	aclProvider         ACLProvider
	canBeReadOnly       bool
}

func (b *curatorFrameworkBuilder) Build() CuratorFramework {
	return newCuratorFramework(b)
}

func (b *curatorFrameworkBuilder) WithDial(zookeeperDialer ZookeeperDialer) CuratorFrameworkBuilder {
	b.zookeeperDialer = zookeeperDialer

	return b
}

func (b *curatorFrameworkBuilder) Authorization(scheme string, auth []byte) CuratorFrameworkBuilder {
	return b.Authorizations(&authInfo{scheme, auth})
}

func (b *curatorFrameworkBuilder) Authorizations(authInfos ...AuthInfo) CuratorFrameworkBuilder {
	b.authInfos = append(b.authInfos, authInfos...)

	return b
}

func (b *curatorFrameworkBuilder) ConnectString(connectString string) CuratorFrameworkBuilder {
	b.ensembleProvider = &fixedEnsembleProvider{connectString}

	return b
}

func (b *curatorFrameworkBuilder) EnsembleProvider(ensembleProvider EnsembleProvider) CuratorFrameworkBuilder {
	b.ensembleProvider = ensembleProvider

	return b
}

func (b *curatorFrameworkBuilder) DefaultData(defaultData []byte) CuratorFrameworkBuilder {
	b.defaultData = defaultData

	return b
}

func (b *curatorFrameworkBuilder) Namespace(namespace string) CuratorFrameworkBuilder {
	b.namespace = namespace

	return b
}

func (b *curatorFrameworkBuilder) SessionTimeout(sessionTimeout time.Duration) CuratorFrameworkBuilder {
	b.sessionTimeout = sessionTimeout

	return b
}

func (b *curatorFrameworkBuilder) ConnectionTimeout(connectionTimeout time.Duration) CuratorFrameworkBuilder {
	b.connectionTimeout = connectionTimeout

	return b
}

func (b *curatorFrameworkBuilder) MaxCloseWait(maxCloseWait time.Duration) CuratorFrameworkBuilder {
	b.maxCloseWait = maxCloseWait

	return b
}

func (b *curatorFrameworkBuilder) RetryPolicy(retryPolicy RetryPolicy) CuratorFrameworkBuilder {
	b.retryPolicy = retryPolicy

	return b
}

func (b *curatorFrameworkBuilder) CompressionProvider(compressionProvider CompressionProvider) CuratorFrameworkBuilder {
	b.compressionProvider = compressionProvider

	return b
}

func (b *curatorFrameworkBuilder) ACLProvider(aclProvider ACLProvider) CuratorFrameworkBuilder {
	b.aclProvider = aclProvider

	return b
}

func (b *curatorFrameworkBuilder) CanBeReadOnly(canBeReadOnly bool) CuratorFrameworkBuilder {
	b.canBeReadOnly = canBeReadOnly

	return b
}

type curatorFramework struct {
	client                  *CuratorZookeeperClient
	stateManager            *ConnectionStateManager
	state                   CuratorFrameworkState
	listeners               CuratorListenable
	unhandledErrorListeners UnhandledErrorListenable
	defaultData             []byte
	retryPolicy             RetryPolicy
	compressionProvider     CompressionProvider
	aclProvider             ACLProvider
}

func newCuratorFramework(b *curatorFrameworkBuilder) *curatorFramework {
	c := &curatorFramework{
		listeners:               NewCuratorListenerContainer(),
		unhandledErrorListeners: NewUnhandledErrorListenerContainer(),
		defaultData:             b.defaultData,
		retryPolicy:             b.retryPolicy,
		compressionProvider:     b.compressionProvider,
		aclProvider:             b.aclProvider,
	}

	watcher := func(event *zk.Event) {
		c.processEvent(&curatorEvent{
			eventType:    WATCHED,
			err:          event.Err,
			path:         c.unfixForNamespace(event.Path),
			watchedEvent: event,
		})
	}

	c.client = NewCuratorZookeeperClient(b.zookeeperDialer, b.ensembleProvider, b.sessionTimeout, b.connectionTimeout, watcher, b.retryPolicy, b.canBeReadOnly)
	c.stateManager = NewConnectionStateManager(c)

	return c
}

func (c *curatorFramework) Start() error {
	if !c.state.Change(LATENT, STARTED) {
		return fmt.Errorf("Cannot be started more than once")
	} else if err := c.stateManager.Start(); err != nil {
		return fmt.Errorf("fail to start state manager, %s", err)
	} else if err := c.client.Start(); err != nil {
		return fmt.Errorf("fail to start client, %s", err)
	}

	return nil
}

func (c *curatorFramework) Close() error {
	if !c.state.Change(STARTED, STOPPED) {
		return nil
	}

	evt := &curatorEvent{eventType: CLOSING}

	c.listeners.ForEach(func(listener CuratorListener) error {
		return listener.EventReceived(c, evt)
	})

	c.listeners.Clear()
	c.unhandledErrorListeners.Clear()

	if err := c.stateManager.Close(); err != nil {
		glog.Errorf("fail to close state manager, %s", err)
	}

	return c.client.Close()
}

func (c *curatorFramework) State() CuratorFrameworkState {
	return c.state.Value()
}

func (c *curatorFramework) Started() bool {
	return c.State() == STARTED
}

func (c *curatorFramework) Create() CreateBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return &createBuilder{client: c}
}

func (c *curatorFramework) Delete() DeleteBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return &checkExistsBuilder{client: c}
}

func (c *curatorFramework) CheckExists() CheckExistsBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) GetData() GetDataBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) SetData() SetDataBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) GetChildren() GetChildrenBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) GetACL() GetACLBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) SetACL() SetACLBuilder {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) InTransaction() CuratorTransaction {
	c.state.Check(STARTED, "instance must be started before calling this method")

	return nil
}

func (c *curatorFramework) ConnectionStateListenable() ConnectionStateListenable {
	return c.stateManager.listeners
}

func (c *curatorFramework) CuratorListenable() CuratorListenable {
	return c.listeners
}

func (c *curatorFramework) UnhandledErrorListenable() UnhandledErrorListenable {
	return c.unhandledErrorListeners
}

func (c *curatorFramework) ZookeeperClient() *CuratorZookeeperClient {
	return c.client
}

func (c *curatorFramework) processEvent(event CuratorEvent) {
	if event.Type() == WATCHED {

	}

}

func (c *curatorFramework) fixForNamespace(path string, isSequential bool) string {
	return path
}

func (c *curatorFramework) unfixForNamespace(path string) string {
	return path
}

func (c *curatorFramework) getNamespaceWatcher(watcher Watcher) Watcher {
	return watcher
}
<|endoftext|>


<commit_before>/**
 * MediaType
 *
 * Copyright © 2014 Trevor N. Suarez (Rican7)
 */

// Tests for the mediatype mutable structure

package mediatype

import (
	"testing"
)

/**
 * Tests functions
 */

func TestMainType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.MainType() != "application" {
			t.Errorf("Incorrect main type for %+v", mt)
		}
	}
}

func TestSubType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.SubType() != "kml" {
			t.Errorf("Incorrect sub type for %+v", mt)
		}
	}
}

func TestTrees(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {
		correctTrees := []string{"vnd", "google-earth"}

		for i, tree := range mt.Trees() {
			if tree != correctTrees[i] {
				t.Errorf("Incorrect trees for %+v", mt)
			}
		}
	}
}

func TestPrefix(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.Prefix() != "vnd" {
			t.Errorf("Incorrect prefix for %+v", mt)
		}
	}
}

func TestSuffix(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.Suffix() != "xml" {
			t.Errorf("Incorrect suffix for %+v", mt)
		}
	}
}

func TestParameters(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {
		correctParameters := map[string]string{"charset": "utf-8"}

		for i, tree := range mt.Parameters() {
			if tree != correctParameters[i] {
				t.Errorf("Incorrect parameters for %+v", mt)
			}
		}
	}
}

func TestFullType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "application/vnd.google-earth.kml+xml" {
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestFullTypeWithSimpleMainAndSub(t *testing.T) {
	mt, err := Parse("application/json")

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "application/json" {
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestString(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.String() != validComplexMediaType {
			t.Errorf("Incorrect string for %+v", mt)
		}
	}
}
<commit_msg>More tests<commit_after>/**
 * MediaType
 *
 * Copyright © 2014 Trevor N. Suarez (Rican7)
 */

// Tests for the mediatype mutable structure

package mediatype

import (
	"testing"
)

/**
 * Tests functions
 */

func TestMainType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.MainType() != "application" {
			t.Errorf("Incorrect main type for %+v", mt)
		}
	}
}

func TestSubType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.SubType() != "kml" {
			t.Errorf("Incorrect sub type for %+v", mt)
		}
	}
}

func TestTrees(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {
		correctTrees := []string{"vnd", "google-earth"}

		for i, tree := range mt.Trees() {
			if tree != correctTrees[i] {
				t.Errorf("Incorrect trees for %+v", mt)
			}
		}
	}
}

func TestPrefix(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.Prefix() != "vnd" {
			t.Errorf("Incorrect prefix for %+v", mt)
		}
	}
}

func TestSuffix(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.Suffix() != "xml" {
			t.Errorf("Incorrect suffix for %+v", mt)
		}
	}
}

func TestParameters(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {
		correctParameters := map[string]string{"charset": "utf-8"}

		for i, tree := range mt.Parameters() {
			if tree != correctParameters[i] {
				t.Errorf("Incorrect parameters for %+v", mt)
			}
		}
	}
}

func TestFullType(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "application/vnd.google-earth.kml+xml" {
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestFullTypeWithSimpleMain(t *testing.T) {
	mt, err := Parse("application")

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "application" {
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestFullTypeWithSimpleMainAndSub(t *testing.T) {
	mt, err := Parse("application/json")

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "application/json" {
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestFullTypeWithSimpleMainAndSubAndTrees(t *testing.T) {
	mt, err := Parse("image/vnd.djvu")

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "image/vnd.djvu" {
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestFullTypeWithSimpleMainAndSubAndPrefix(t *testing.T) {
	mt, err := Parse("application/xhtml+xml")

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.FullType() != "application/xhtml+xml" {
			t.Errorf("Incorrect full type for %+v", mt)
		}
	}
}

func TestString(t *testing.T) {
	mt, err := Parse(validComplexMediaType)

	if nil != err {
		t.Errorf("Parsing failed for valid '%s'", validComplexMediaType)
	} else {

		if mt.String() != validComplexMediaType {
			t.Errorf("Incorrect string for %+v", mt)
		}
	}
}
<|endoftext|>


<commit_before>// An experimental FTP server framework. By providing a simple driver class that
// responds to a handful of methods you can have a complete FTP server.
//
// Some sample use cases include persisting data to an Amazon S3 bucket, a
// relational database, redis or memory.
//
// There is a sample in-memory driver available - see the documentation for the
// graval-mem package or the graval READEME for more details.
package graval

import (
	"net"
	"strconv"
	"strings"
)

// serverOpts contains parameters for graval.NewFTPServer()
type FTPServerOpts struct {
	// The factory that will be used to create a new FTPDriver instance for
	// each client connection. This is a mandatory option.
	Factory   FTPDriverFactory

	// The hostname that the FTP server should listen on. Optional, defaults to
	// "::", which means all hostnames on ipv4 and ipv6.
	Hostname  string

	// The port that the FTP should listen on. Optional, defaults to 3000. In
	// a production environment you will probably want to change this to 21.
	Port      int
}

// FTPServer is the root of your FTP application. You should instantiate one
// of these and call ListenAndServe() to start accepting client connections.
//
// Always use the NewFTPServer() method to create a new FTPServer.
type FTPServer struct {
	name          string
	listenTo      string
	driverFactory FTPDriverFactory
	logger        *ftpLogger
}

// serverOptsWithDefaults copies an FTPServerOpts struct into a new struct,
// then adds any default values that are missing and returns the new data.
func serverOptsWithDefaults(opts *FTPServerOpts) *FTPServerOpts {
	var newOpts FTPServerOpts
	if opts == nil {
		opts = &FTPServerOpts{}
	}
	if opts.Hostname == "" {
		newOpts.Hostname = "::"
	} else {
		newOpts.Hostname = opts.Hostname
	}
	if opts.Port == 0 {
		newOpts.Port = 3000
	} else {
		newOpts.Port = opts.Port
	}
	newOpts.Factory = opts.Factory

	return &newOpts
}

// NewFTPServer initialises a new FTP server. Configuration options are provided
// via an instance of FTPServerOpts. Calling this function in your code will
// probably look something like this:
//
//     factory := &MyDriverFactory{}
//     server  := graval.NewFTPServer(&graval.FTPServerOpts{ Factory: factory })
//
// or:
//
//     factory := &MyDriverFactory{}
//     opts    := &graval.FTPServerOpts{
//       Factory: factory,
//       Port: 2000,
//       Hostname: "127.0.0.1",
//     }
//     server  := graval.NewFTPServer(opts)
//
func NewFTPServer(opts *FTPServerOpts) *FTPServer {
	opts = serverOptsWithDefaults(opts)
	s := new(FTPServer)
	s.listenTo = buildTcpString(opts.Hostname, opts.Port)
	s.name = "Go FTP Server"
	s.driverFactory = opts.Factory
	s.logger = newFtpLogger("")
	return s
}

// ListenAndServe asks a new FTPServer to begin accepting client connections. It
// accepts no arguments - all configuration is provided via the NewFTPServer
// function.
//
// If the server fails to start for any reason, an error will be returned. Common
// errors are trying to bind to a privileged port or something else is already
// listening on the same port.
//
func (ftpServer *FTPServer) ListenAndServe() error {
	laddr, err := net.ResolveTCPAddr("tcp", ftpServer.listenTo)
	if err != nil {
		return err
	}
	listener, err := net.ListenTCP("tcp", laddr)
	if err != nil {
		return err
	}
	for {
		tcpConn, err := listener.AcceptTCP()
		if err != nil {
			ftpServer.logger.Print("listening error")
			break
		}
		driver, err := ftpServer.driverFactory.NewDriver()
		if err != nil {
			ftpServer.logger.Print("Error creating driver, aborting client connection")
		} else {
			ftpConn := newftpConn(tcpConn, driver)
			go ftpConn.Serve()
		}
	}
	return nil
}

func buildTcpString(hostname string, port int) (result string) {
	if strings.Contains(hostname, ":") {
		// ipv6
		if port == 0 {
			result = "["+hostname+"]"
		} else {
			result = "["+hostname +"]:" + strconv.Itoa(port)
		}
	} else {
		// ipv4
		if port == 0 {
			result = hostname
		} else {
			result = hostname +":" + strconv.Itoa(port)
		}
	}
	return
}
<commit_msg>on boot, print the address and port we're listening on<commit_after>// An experimental FTP server framework. By providing a simple driver class that
// responds to a handful of methods you can have a complete FTP server.
//
// Some sample use cases include persisting data to an Amazon S3 bucket, a
// relational database, redis or memory.
//
// There is a sample in-memory driver available - see the documentation for the
// graval-mem package or the graval READEME for more details.
package graval

import (
	"net"
	"strconv"
	"strings"
)

// serverOpts contains parameters for graval.NewFTPServer()
type FTPServerOpts struct {
	// The factory that will be used to create a new FTPDriver instance for
	// each client connection. This is a mandatory option.
	Factory   FTPDriverFactory

	// The hostname that the FTP server should listen on. Optional, defaults to
	// "::", which means all hostnames on ipv4 and ipv6.
	Hostname  string

	// The port that the FTP should listen on. Optional, defaults to 3000. In
	// a production environment you will probably want to change this to 21.
	Port      int
}

// FTPServer is the root of your FTP application. You should instantiate one
// of these and call ListenAndServe() to start accepting client connections.
//
// Always use the NewFTPServer() method to create a new FTPServer.
type FTPServer struct {
	name          string
	listenTo      string
	driverFactory FTPDriverFactory
	logger        *ftpLogger
}

// serverOptsWithDefaults copies an FTPServerOpts struct into a new struct,
// then adds any default values that are missing and returns the new data.
func serverOptsWithDefaults(opts *FTPServerOpts) *FTPServerOpts {
	var newOpts FTPServerOpts
	if opts == nil {
		opts = &FTPServerOpts{}
	}
	if opts.Hostname == "" {
		newOpts.Hostname = "::"
	} else {
		newOpts.Hostname = opts.Hostname
	}
	if opts.Port == 0 {
		newOpts.Port = 3000
	} else {
		newOpts.Port = opts.Port
	}
	newOpts.Factory = opts.Factory

	return &newOpts
}

// NewFTPServer initialises a new FTP server. Configuration options are provided
// via an instance of FTPServerOpts. Calling this function in your code will
// probably look something like this:
//
//     factory := &MyDriverFactory{}
//     server  := graval.NewFTPServer(&graval.FTPServerOpts{ Factory: factory })
//
// or:
//
//     factory := &MyDriverFactory{}
//     opts    := &graval.FTPServerOpts{
//       Factory: factory,
//       Port: 2000,
//       Hostname: "127.0.0.1",
//     }
//     server  := graval.NewFTPServer(opts)
//
func NewFTPServer(opts *FTPServerOpts) *FTPServer {
	opts = serverOptsWithDefaults(opts)
	s := new(FTPServer)
	s.listenTo = buildTcpString(opts.Hostname, opts.Port)
	s.name = "Go FTP Server"
	s.driverFactory = opts.Factory
	s.logger = newFtpLogger("")
	return s
}

// ListenAndServe asks a new FTPServer to begin accepting client connections. It
// accepts no arguments - all configuration is provided via the NewFTPServer
// function.
//
// If the server fails to start for any reason, an error will be returned. Common
// errors are trying to bind to a privileged port or something else is already
// listening on the same port.
//
func (ftpServer *FTPServer) ListenAndServe() error {
	laddr, err := net.ResolveTCPAddr("tcp", ftpServer.listenTo)
	if err != nil {
		return err
	}
	listener, err := net.ListenTCP("tcp", laddr)
	if err != nil {
		return err
	}
	ftpServer.logger.Printf("listening on %s", listener.Addr().String())

	for {
		tcpConn, err := listener.AcceptTCP()
		if err != nil {
			ftpServer.logger.Print("listening error")
			break
		}
		driver, err := ftpServer.driverFactory.NewDriver()
		if err != nil {
			ftpServer.logger.Print("Error creating driver, aborting client connection")
		} else {
			ftpConn := newftpConn(tcpConn, driver)
			go ftpConn.Serve()
		}
	}
	return nil
}

func buildTcpString(hostname string, port int) (result string) {
	if strings.Contains(hostname, ":") {
		// ipv6
		if port == 0 {
			result = "["+hostname+"]"
		} else {
			result = "["+hostname +"]:" + strconv.Itoa(port)
		}
	} else {
		// ipv4
		if port == 0 {
			result = hostname
		} else {
			result = hostname +":" + strconv.Itoa(port)
		}
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2016 e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package diff implements diff functions to compare objects.
package diff

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

// A Diff represents the changes between two structures.
type Diff map[string]interface{}

// PrettyJSON serializes the Diff into JSON in a indented (hence human readable)
// way.
func (d Diff) PrettyJSON() []byte {
	bs, err := json.MarshalIndent(d, "", "   ")
	if err != nil {
		return []byte{}
	}
	return bs
}

// JSON serializes the Diff into JSON in a compact format (no indentation).
func (d Diff) JSON() []byte {
	bs, err := json.MarshalIndent(d, "", "   ")
	if err != nil {
		return []byte{}
	}
	return bs
}

type ChangeType string

// Possible values for a ChangeType.
const (
	AddType ChangeType = "ADD" // addition
	DelType ChangeType = "DEL" // deletion
	ModType ChangeType = "MOD" // modification
)

type Change struct {
	OldVal interface{} `json:"old_value"`
	NewVal interface{} `json:"new_value"`
}

type CollectionChange struct {
	Val  interface{} `json:"value"`
	Type ChangeType  `json:"type"`
}

func Compute(x, y interface{}, recursive bool) (Diff, error) {
	vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
	tx, ty := vx.Type(), vy.Type()

	if !tx.AssignableTo(ty) {
		return nil, errors.New("input objects do not share the same type")
	}

	// since x and y share the same type, there is no need to check them both
	if vx.Kind() != reflect.Struct {
		return nil, errors.New("input values are not struct")
	}

	xNumFields := vx.NumField()

	delta := make(Diff)

	for i := 0; i < xNumFields; i++ {
		fx := vx.Field(i)
		typ := tx.Field(i)
		if !isExported(typ.Name) { // skip non-exported fields
			continue
		}
		fy := vy.FieldByName(typ.Name)

		if d := handleValue(fx, fy); d != nil {
			delta[typ.Name] = d
		}
	}

	return delta, nil
}

func handleValue(fx, fy reflect.Value) interface{} {
	switch fx.Kind() {

	case reflect.Struct:
		return handleStruct(fx, fy)
	case reflect.Array, reflect.Slice:
		return handleSlice(fx, fy)
	case reflect.Map:
		return nil

	case reflect.Ptr:
		if fx.IsNil() {
			if fy.IsNil() {
				return nil
			}
			return Change{OldVal: nil, NewVal: fy.Elem().Interface()}
		} else if fy.IsNil() {
			return Change{OldVal: fx.Elem().Interface(), NewVal: nil}
		}
		return handleValue(fx.Elem(), fy.Elem())

	case reflect.Interface, reflect.Func, reflect.Chan, reflect.Invalid, reflect.UnsafePointer, reflect.Complex64, reflect.Complex128:
		// TODO(gilliek): support complex numbers
		return nil

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		ix, iy := fx.Int(), fy.Int()
		if ix != iy {
			return Change{OldVal: ix, NewVal: iy}
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		uix, uiy := fx.Uint(), fy.Uint()
		if uix != uiy {
			return Change{OldVal: uix, NewVal: uiy}
		}
	case reflect.Float32, reflect.Float64:
		flx, fly := fx.Float(), fy.Float()
		if flx-fly < 0.000001 {
			return Change{OldVal: flx, NewVal: fly}
		}
	case reflect.String:
		sx, sy := fx.String(), fy.String()
		if sx != sy {
			return Change{OldVal: sx, NewVal: sy}
		}
	}

	return nil
}

func handleStruct(fx, fy reflect.Value) interface{} {
	if isFullyNonExportedStruct(fx) {
		if !isEqual(fx, fy) {
			return Change{OldVal: fx.Interface(), NewVal: fy.Interface()}
		}
		return nil
	}

	delta := make(Diff)
	numFields := fx.NumField()
	for i := 0; i < numFields; i++ {
		newFx := fx.Field(i)
		typ := fx.Type().Field(i)
		if !isExported(typ.Name) { // skip non-exported fields
			continue
		}
		newFy := fy.FieldByName(typ.Name)

		if d := handleValue(newFx, newFy); d != nil {
			delta[typ.Name] = d
		}
	}
	if len(delta) > 0 {
		return delta
	}
	return nil
}

func handleSlice(fx, fy reflect.Value) interface{} {
	xLen, yLen := fx.Len(), fy.Len()

	changes := make(map[string]CollectionChange)
	if xLen == 0 {
		if yLen == 0 {
			return nil
		}
		for i := 0; i < yLen; i++ {
			changes[strconv.Itoa(i)] = CollectionChange{Val: fy.Index(i).Interface(), Type: AddType}
		}
	} else if yLen == 0 {
		for i := 0; i < xLen; i++ {
			changes[strconv.Itoa(i)] = CollectionChange{Val: fx.Index(i).Interface(), Type: DelType}
		}
	} else {
		var maxLen int
		if xLen > yLen {
			maxLen = yLen
			for i := yLen; i < xLen; i++ {
				changes[strconv.Itoa(i)] = CollectionChange{Val: fx.Index(i).Interface(), Type: DelType}
			}
		} else if xLen < yLen {
			maxLen = xLen
			for i := xLen; i < yLen; i++ {
				changes[strconv.Itoa(i)] = CollectionChange{Val: fy.Index(i).Interface(), Type: AddType}
			}
		}
		for i := 0; i < maxLen; i++ {
			if d := handleValue(fx.Index(i), fy.Index(i)); d != nil {
				changes[strconv.Itoa(i)] = CollectionChange{Val: d, Type: ModType}
			}
		}
	}
	if len(changes) > 0 {
		return changes
	}
	return nil
}

func isExported(fieldName string) bool {
	if fieldName == "" {
		return false
	}
	firstLetter := string(fieldName[0])
	return firstLetter != strings.ToLower(firstLetter)
}

func isFullyNonExportedStruct(s reflect.Value) bool {
	if s.Kind() != reflect.Struct {
		return false
	}
	numFields := s.NumField()
	for i := 0; i < numFields; i++ {
		typ := s.Type().Field(i)
		if isExported(typ.Name) {
			return false
		}
	}
	return true
}

func isEqual(x, y reflect.Value) bool {
	return fmt.Sprint(x.Interface()) == fmt.Sprint(y.Interface())
}
<commit_msg>diff: improve package exposed API<commit_after>// Copyright 2016 e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package diff implements diff functions to compare objects.
package diff

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

// A Diff represents the changes between two structures.
type Diff map[string]interface{}

// HasChange report whether the Diff contains some changes.
func (d Diff) HasChange() bool {
	return len(d) > 0
}

// PrettyJSON serializes the Diff into JSON in a indented (hence human readable)
// way.
func (d Diff) PrettyJSON() []byte {
	bs, err := json.MarshalIndent(d, "", "   ")
	if err != nil {
		return []byte{}
	}
	return bs
}

// JSON serializes the Diff into JSON in a compact format (no indentation).
func (d Diff) JSON() []byte {
	bs, err := json.MarshalIndent(d, "", "   ")
	if err != nil {
		return []byte{}
	}
	return bs
}

type ChangeType string

// Possible values for a ChangeType.
const (
	AddType ChangeType = "ADD" // addition
	DelType ChangeType = "DEL" // deletion
	ModType ChangeType = "MOD" // modification
)

type Change struct {
	OldVal interface{} `json:"old_value"`
	NewVal interface{} `json:"new_value"`
}

type CollectionChange struct {
	Val  interface{} `json:"value"`
	Type ChangeType  `json:"type"`
}

func Compute(x, y interface{}, recursive bool) (Diff, error) {
	vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
	tx, ty := vx.Type(), vy.Type()

	if !tx.AssignableTo(ty) {
		return nil, errors.New("input objects do not share the same type")
	}

	// since x and y share the same type, there is no need to check them both
	if vx.Kind() != reflect.Struct {
		return nil, errors.New("input values are not struct")
	}

	xNumFields := vx.NumField()

	delta := make(Diff)

	for i := 0; i < xNumFields; i++ {
		fx := vx.Field(i)
		typ := tx.Field(i)
		if !isExported(typ.Name) { // skip non-exported fields
			continue
		}
		fy := vy.FieldByName(typ.Name)

		if d := handleValue(fx, fy); d != nil {
			delta[typ.Name] = d
		}
	}

	return delta, nil
}

func handleValue(fx, fy reflect.Value) interface{} {
	switch fx.Kind() {

	case reflect.Struct:
		return handleStruct(fx, fy)
	case reflect.Array, reflect.Slice:
		return handleSlice(fx, fy)
	case reflect.Map:
		return nil

	case reflect.Ptr:
		if fx.IsNil() {
			if fy.IsNil() {
				return nil
			}
			return Change{OldVal: nil, NewVal: fy.Elem().Interface()}
		} else if fy.IsNil() {
			return Change{OldVal: fx.Elem().Interface(), NewVal: nil}
		}
		return handleValue(fx.Elem(), fy.Elem())

	case reflect.Interface, reflect.Func, reflect.Chan, reflect.Invalid, reflect.UnsafePointer, reflect.Complex64, reflect.Complex128:
		// TODO(gilliek): support complex numbers
		return nil

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		ix, iy := fx.Int(), fy.Int()
		if ix != iy {
			return Change{OldVal: ix, NewVal: iy}
		}
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		uix, uiy := fx.Uint(), fy.Uint()
		if uix != uiy {
			return Change{OldVal: uix, NewVal: uiy}
		}
	case reflect.Float32, reflect.Float64:
		flx, fly := fx.Float(), fy.Float()
		if flx-fly < 0.000001 {
			return Change{OldVal: flx, NewVal: fly}
		}
	case reflect.String:
		sx, sy := fx.String(), fy.String()
		if sx != sy {
			return Change{OldVal: sx, NewVal: sy}
		}
	}

	return nil
}

func handleStruct(fx, fy reflect.Value) interface{} {
	if isFullyNonExportedStruct(fx) {
		if !isEqual(fx, fy) {
			return Change{OldVal: fx.Interface(), NewVal: fy.Interface()}
		}
		return nil
	}

	delta := make(Diff)
	numFields := fx.NumField()
	for i := 0; i < numFields; i++ {
		newFx := fx.Field(i)
		typ := fx.Type().Field(i)
		if !isExported(typ.Name) { // skip non-exported fields
			continue
		}
		newFy := fy.FieldByName(typ.Name)

		if d := handleValue(newFx, newFy); d != nil {
			delta[typ.Name] = d
		}
	}
	if len(delta) > 0 {
		return delta
	}
	return nil
}

func handleSlice(fx, fy reflect.Value) interface{} {
	xLen, yLen := fx.Len(), fy.Len()

	changes := make(map[string]CollectionChange)
	if xLen == 0 {
		if yLen == 0 {
			return nil
		}
		for i := 0; i < yLen; i++ {
			changes[strconv.Itoa(i)] = CollectionChange{Val: fy.Index(i).Interface(), Type: AddType}
		}
	} else if yLen == 0 {
		for i := 0; i < xLen; i++ {
			changes[strconv.Itoa(i)] = CollectionChange{Val: fx.Index(i).Interface(), Type: DelType}
		}
	} else {
		var maxLen int
		if xLen > yLen {
			maxLen = yLen
			for i := yLen; i < xLen; i++ {
				changes[strconv.Itoa(i)] = CollectionChange{Val: fx.Index(i).Interface(), Type: DelType}
			}
		} else if xLen < yLen {
			maxLen = xLen
			for i := xLen; i < yLen; i++ {
				changes[strconv.Itoa(i)] = CollectionChange{Val: fy.Index(i).Interface(), Type: AddType}
			}
		}
		for i := 0; i < maxLen; i++ {
			if d := handleValue(fx.Index(i), fy.Index(i)); d != nil {
				changes[strconv.Itoa(i)] = CollectionChange{Val: d, Type: ModType}
			}
		}
	}
	if len(changes) > 0 {
		return changes
	}
	return nil
}

func isExported(fieldName string) bool {
	if fieldName == "" {
		return false
	}
	firstLetter := string(fieldName[0])
	return firstLetter != strings.ToLower(firstLetter)
}

func isFullyNonExportedStruct(s reflect.Value) bool {
	if s.Kind() != reflect.Struct {
		return false
	}
	numFields := s.NumField()
	for i := 0; i < numFields; i++ {
		typ := s.Type().Field(i)
		if isExported(typ.Name) {
			return false
		}
	}
	return true
}

func isEqual(x, y reflect.Value) bool {
	return fmt.Sprint(x.Interface()) == fmt.Sprint(y.Interface())
}
<|endoftext|>


<commit_before>package validator

import (
	"errors"
	"reflect"
	"strings"
)

var defaultHandler IValidatorHandler

var FieldName func(f reflect.StructField) string

func init() {
	// default
	FieldName = func(f reflect.StructField) string {
		return f.Name
	}
}

func DefaultHandler() IValidatorHandler {
	if defaultHandler == nil {
		defaultHandler = new(Handler)
		defaultHandler.Attach(&Array{})
		defaultHandler.Attach(&Int{})
		defaultHandler.Attach(&Float{})
		defaultHandler.Attach(&String{})
		defaultHandler.Attach(&Map{})
		defaultHandler.Attach(&Interface{})
	}

	return defaultHandler
}

type Handler struct {
	validators []IValidator
}

func (self *Handler) Attach(v IValidator) {
	v.SetHandler(self)
	self.validators = append(self.validators, v)
}

func (self *Handler) ValidateField(f reflect.StructField, fv reflect.Value) (errs []error) {

	if reflect.Struct == f.Type.Kind() {
		if es := self.Validate(fv.Interface(), nil); es != nil {
			errs = append(errs, es...)
		}
	}

	for _, v := range self.validators {
		if v.Filter(f, fv) {
			if es := v.Validate(f, fv); es != nil {
				errs = append(errs, es...)
			}
		}
	}

	return
}

func (self *Handler) Validate(m interface{}, attributes []string) (errs []error) {
	refType := reflect.TypeOf(m)
	refValue := reflect.ValueOf(m)
	if refType.Kind() == reflect.Ptr {
		refType = refType.Elem()
		refValue = refValue.Elem()
	}

	if refType.Kind() != reflect.Struct {
		return []error{errors.New("The validate m must be struct or ptr")}
	}

	if im, ok := m.(IBeforeValidateModel); ok {
		if es := im.BeforeValidate(); es != nil {
			errs = append(errs, es...)
		}
	}

	if attributes != nil {
		for _, name := range attributes {
			//Handle embedded attributes, for example: Profile.SecondaryEmail or event Comments.Id
			if _names := strings.SplitAfterN(name, ".", 2); 1 < len(_names) {
				attr := strings.Trim(_names[0], ".")
				if attrT, ok := refType.FieldByName(attr); ok {
					if attrT.Type.Kind() != reflect.Struct {
						errs = append(errs, errors.New(attr+" is not a struct"))
						return
					}

					if fv := refValue.FieldByName(attr); fv.IsValid() {
						if _errs := self.Validate(fv.Interface(), []string{_names[1]}); _errs != nil {
							errs = append(errs, _errs...)
						}
					} else {
						errs = append(errs, errors.New(attr+" is not valid"))
					}
				} else {
					errs = append(errs, errors.New(attr+" does not exists"))
				}
			} else { //no ".", so we validate directly
				if f, ok := refType.FieldByName(name); ok {
					if fv := refValue.FieldByName(name); fv.IsValid() {
						if es := self.ValidateField(f, fv); es != nil {
							errs = append(errs, es...)
						}
					} else {
						errs = append(errs, errors.New(name+" does not exists"))
					}
				} else {
					errs = append(errs, errors.New(name+" does not exists"))
				}
			}
		}
	} else {
		for i, numFile := 0, refType.NumField(); i < numFile; i++ {
			f := refType.Field(i)
			fv := refValue.Field(i)
			if es := self.ValidateField(f, fv); es != nil {
				errs = append(errs, es...)
			}
		}
	}

	if im, ok := m.(IValidateModel); ok {
		if es := im.Validate(); es != nil {
			errs = append(errs, es...)
		}
	}

	return
}
<commit_msg>fix the struct field validate does not work<commit_after>package validator

import (
	"errors"
	"reflect"
	"strings"
)

var defaultHandler IValidatorHandler

var FieldName func(f reflect.StructField) string

func init() {
	// default
	FieldName = func(f reflect.StructField) string {
		return f.Name
	}
}

func DefaultHandler() IValidatorHandler {
	if defaultHandler == nil {
		defaultHandler = new(Handler)
		defaultHandler.Attach(&Array{})
		defaultHandler.Attach(&Int{})
		defaultHandler.Attach(&Float{})
		defaultHandler.Attach(&String{})
		defaultHandler.Attach(&Map{})
		defaultHandler.Attach(&Interface{})
	}

	return defaultHandler
}

type Handler struct {
	validators []IValidator
}

func (self *Handler) Attach(v IValidator) {
	v.SetHandler(self)
	self.validators = append(self.validators, v)
}

func (self *Handler) ValidateField(f reflect.StructField, fv reflect.Value) (errs []error) {
	if fv.Type().Kind() == reflect.Ptr {
		fv = fv.Elem()
	}

	if reflect.Struct == fv.Kind() {
		if es := self.Validate(fv.Addr().Interface(), nil); es != nil {
			errs = append(errs, es...)
			return
		}
	}

	for _, v := range self.validators {
		if v.Filter(f, fv) {
			if es := v.Validate(f, fv); es != nil {
				errs = append(errs, es...)
				return
			}
		}
	}

	return
}

func (self *Handler) Validate(m interface{}, attributes []string) (errs []error) {
	refType := reflect.TypeOf(m)
	refValue := reflect.ValueOf(m)
	if refType.Kind() == reflect.Ptr {
		refType = refType.Elem()
		refValue = refValue.Elem()
	}

	if refType.Kind() != reflect.Struct {
		return []error{errors.New("The validate m must be struct or ptr")}
	}

	if im, ok := m.(IBeforeValidateModel); ok {
		if es := im.BeforeValidate(); es != nil {
			return es
		}
	}

	if attributes != nil {
		for _, name := range attributes {
			//Handle embedded attributes, for example: Profile.SecondaryEmail or event Comments.Id
			if _names := strings.SplitAfterN(name, ".", 2); 1 < len(_names) {
				attr := strings.Trim(_names[0], ".")
				if attrT, ok := refType.FieldByName(attr); ok {
					if attrT.Type.Kind() != reflect.Struct {
						errs = append(errs, errors.New(attr+" is not a struct"))
						return
					}

					if fv := refValue.FieldByName(attr); fv.IsValid() {
						if _errs := self.Validate(fv.Interface(), []string{_names[1]}); _errs != nil {
							errs = append(errs, _errs...)
						}
					} else {
						errs = append(errs, errors.New(attr+" is not valid"))
					}
				} else {
					errs = append(errs, errors.New(attr+" does not exists"))
				}
			} else { //no ".", so we validate directly
				if f, ok := refType.FieldByName(name); ok {
					if fv := refValue.FieldByName(name); fv.IsValid() {
						if es := self.ValidateField(f, fv); es != nil {
							errs = append(errs, es...)
						}
					} else {
						errs = append(errs, errors.New(name+" does not exists"))
					}
				} else {
					errs = append(errs, errors.New(name+" does not exists"))
				}
			}
		}
	} else {
		for i, numFile := 0, refType.NumField(); i < numFile; i++ {
			f := refType.Field(i)
			fv := refValue.Field(i)
			if es := self.ValidateField(f, fv); es != nil {
				errs = append(errs, es...)
			}
		}
	}

	if 0 < len(errs) {
		return
	}

	if im, ok := m.(IValidateModel); ok {
		if es := im.Validate(); es != nil {
			errs = append(errs, es...)
		}
	}

	return
}
<|endoftext|>