<commit_before>package build

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jkomoros/boardgame/boardgame-util/lib/config"
	"github.com/jkomoros/boardgame/boardgame-util/lib/path"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

const staticSubFolder = "static"
const configJsFileName = "config.js"
const gameSrcSubFolder = "game-src"
const clientSubFolder = "client"

//The path, relative to goPath, where all of the files are to copy
const staticServerPackage = "github.com/jkomoros/boardgame/server/static/webapp"

var filesToLink []string = []string{
	"bower.json",
	"firebase.json",
	"polymer.json",
	"manifest.json",
	"index.html",
	"src",
	"bower_components",
}

//SimpleStaticServer creates and runs a simple static server. directory is the
//folder that the `static` folder is contained within. If no error is
//returned, Runs until the program exits.
func SimpleStaticServer(directory string, port string) error {

	staticPath := filepath.Join(directory, staticSubFolder)

	if _, err := os.Stat(staticPath); os.IsNotExist(err) {
		return errors.New(staticPath + " does not exist")
	}

	fs := http.FileServer(http.Dir(staticPath))

	infos, err := ioutil.ReadDir(staticPath)

	if err != nil {
		return errors.New("Couldn't enumerate items in serving path")
	}

	//Install specific handlers for each existing file or directory in the
	//path to serve.
	for _, info := range infos {
		if info.Name() == "index.html" {
			continue
		}
		name := "/" + info.Name()
		if info.IsDir() {
			name += "/"
		}
		http.Handle(name, fs)
	}

	//This pattern will match as fallback (it's the shortest), and should
	//return "index.html" for everythign that doesn't match one of the ones
	//already returned.
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		//Safe to use since "index.html" is not provided by user but is a constant
		http.ServeFile(w, r, filepath.Join(staticPath, "index.html"))
	})

	return http.ListenAndServe(":"+port, nil)

}

//Static creates a folder of static resources for serving within the static
//subfolder of directory. It symlinks necessary resources in. The return value
//is the directory where the assets can be served from, and an error if there
//was an error. You can clean up the created folder structure with CleanStatic.
func Static(directory string, managers []string, c *config.Config) (assetRoot string, err error) {

	if err := ensureBowerComponents(); err != nil {
		return "", errors.New("bower_components couldn't be created: " + err.Error())
	}

	if _, err := os.Stat(directory); os.IsNotExist(err) {
		return "", errors.New(directory + " did not already exist.")
	}

	staticDir := filepath.Join(directory, staticSubFolder)

	if _, err := os.Stat(staticDir); os.IsNotExist(err) {
		if err := os.Mkdir(staticDir, 0700); err != nil {
			return "", errors.New("Couldn't create static directory: " + err.Error())
		}
	}

	fullPkgPath, err := path.AbsoluteGoPkgPath(staticServerPackage)

	if err != nil {
		return "", errors.New("Couldn't get full package path: " + err.Error())
	}

	//TODO: some of the config files should be copied not symlinked; some of
	//these folders will stay around. Maybe take a temp parameter about
	//whehter it should do copying or not.

	workingDirectory, err := os.Getwd()

	if err != nil {
		return "", errors.New("Can't get working directory: " + err.Error())
	}

	for _, name := range filesToLink {
		localPath := filepath.Join(staticDir, name)
		absLocalDirPath := filepath.Join(workingDirectory, staticDir) + string(filepath.Separator)
		absRemotePath := filepath.Join(fullPkgPath, name)

		relRemotePath, err := path.RelativizePaths(absLocalDirPath, absRemotePath)

		rejoinedPath := filepath.Join(absLocalDirPath, relRemotePath)

		if _, err := os.Stat(rejoinedPath); os.IsNotExist(err) {

			if strings.Contains(name, "bower") {
				return "", errors.New("bower_components doesn't appear to exist. You may need to run `bower update` from within `boardgame/server/static/webapp`.")
			}

			return "", errors.New("Unexpected error: relRemotePath of " + relRemotePath + " doesn't exist " + absLocalDirPath + " : " + absRemotePath + "(" + rejoinedPath + ")")
		}

		if err != nil {
			return "", errors.New("Couldn't relativize paths: " + err.Error())
		}

		if _, err := os.Stat(localPath); err == nil {
			//Must already exist, so can skip
			continue
		}
		fmt.Println("Linking " + localPath + " to " + relRemotePath)
		if err := os.Symlink(relRemotePath, localPath); err != nil {
			return "", errors.New("Couldn't link " + name + ": " + err.Error())
		}

	}

	fmt.Println("Creating " + configJsFileName)
	if err := createConfigJs(filepath.Join(staticDir, configJsFileName), c); err != nil {
		return "", errors.New("Couldn't create " + configJsFileName + ": " + err.Error())
	}

	fmt.Println("Creating " + gameSrcSubFolder)
	if err := linkGameClientFolders(staticDir, managers); err != nil {
		return "", errors.New("Couldn't create " + gameSrcSubFolder + ": " + err.Error())
	}

	return staticDir, nil

}

//ensureBowerComoonents ensures that
//`$GOPATH/src/github.com/jkomoros/boardgame/server/static/webapp` has bower
//components.
func ensureBowerComponents() error {

	p, err := path.AbsoluteGoPkgPath(staticServerPackage)

	if err != nil {
		return err
	}

	if _, err := os.Stat(filepath.Join(p, "bower_components")); err == nil {
		//It appears to exist, we're fine!
		return nil
	}

	_, err = exec.LookPath("bower")

	if err != nil {
		return errors.New("bower_components didn't exist and bower didn't appear to be installed. You need to install bower.")
	}

	cmd := exec.Command("bower", "update")
	cmd.Dir = p
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	fmt.Println("bower_components didn't exist, running `bower update`...")
	if err := cmd.Run(); err != nil {
		return errors.New("Couldn't `bower update`: " + err.Error())
	}

	return nil

}

//linkGameClientFolders creates a game-src within basePath and then links the
//client folders for each one.
func linkGameClientFolders(basePath string, managers []string) error {

	if _, err := os.Stat(basePath); os.IsNotExist(err) {
		return errors.New(basePath + " doesn't exist")
	}

	gameSrcDir := filepath.Join(basePath, gameSrcSubFolder)

	if _, err := os.Stat(gameSrcDir); os.IsNotExist(err) {
		if err := os.Mkdir(gameSrcDir, 0700); err != nil {
			return errors.New("Couldn't create game-src directory: " + err.Error())
		}
	}

	workingDirectory, err := os.Getwd()

	if err != nil {
		return errors.New("Can't get working directory: " + err.Error())
	}

	for _, manager := range managers {
		absPkgPath, err := path.AbsoluteGoPkgPath(manager)

		if err != nil {
			return errors.New("Couldn't generate absPkgPath for " + manager + ": " + err.Error())
		}

		pkgShortName := filepath.Base(manager)

		absClientPath := filepath.Join(absPkgPath, clientSubFolder, pkgShortName)

		if _, err := os.Stat(absClientPath); os.IsNotExist(err) {
			fmt.Println("Skipping " + manager + " because it doesn't appear to have a client sub-directory")
			continue
		}

		relLocalPath := filepath.Join(gameSrcDir, pkgShortName)

		//This feels like it should be relLocalPath, but it needs to be
		//gameSrcDir, otherwise there's an extra ".." in the path. Not really
		//sure why. :-/
		absLocalPath := filepath.Join(workingDirectory, gameSrcDir)

		relPath, err := path.RelativizePaths(absLocalPath, absClientPath)

		if err != nil {
			return errors.New("Couldn't relativize path: " + err.Error())
		}

		rejoinedPath := filepath.Join(absLocalPath, relPath)

		if _, err := os.Stat(rejoinedPath); os.IsNotExist(err) {
			return errors.New("Unexpected error: relPath of " + relPath + " doesn't exist " + absLocalPath + " : " + absClientPath + "(" + rejoinedPath + ")")
		}

		if _, err := os.Stat(relLocalPath); err == nil {
			//Must already exist, so can skip
			continue
		}

		fmt.Println("Linking " + relLocalPath + " to " + relPath)
		if err := os.Symlink(relPath, relLocalPath); err != nil {
			return errors.New("Couldn't create sym lnk for " + manager + ": " + relPath + ":: " + relLocalPath)
		}

	}

	return nil

}

func createConfigJs(path string, c *config.Config) error {
	client := c.Client(false)

	clientBlob, err := json.MarshalIndent(client, "", "\t")

	if err != nil {
		return errors.New("Couldn't create blob: " + err.Error())
	}

	fileContents := "var CONFIG = " + string(clientBlob)

	if err := ioutil.WriteFile(path, []byte(fileContents), 0644); err != nil {
		return errors.New("Couldn't create file: " + err.Error())
	}

	return nil

}

//CleanStatic removes all of the things created in the static subfolder within
//directory.
func CleanStatic(directory string) error {
	return os.RemoveAll(filepath.Join(directory, staticSubFolder))
}
<commit_msg>Respond to index.html to all resquests that are otherwise unhandled. BREAKs server because images and serverworker.js don't exist. Part of #655.<commit_after>package build

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/jkomoros/boardgame/boardgame-util/lib/config"
	"github.com/jkomoros/boardgame/boardgame-util/lib/path"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

const staticSubFolder = "static"
const configJsFileName = "config.js"
const gameSrcSubFolder = "game-src"
const clientSubFolder = "client"

//The path, relative to goPath, where all of the files are to copy
const staticServerPackage = "github.com/jkomoros/boardgame/server/static/webapp"

var filesToLink []string = []string{
	"bower.json",
	"firebase.json",
	"polymer.json",
	"manifest.json",
	"index.html",
	"src",
	"bower_components",
}

//SimpleStaticServer creates and runs a simple static server. directory is the
//folder that the `static` folder is contained within. If no error is
//returned, Runs until the program exits.
func SimpleStaticServer(directory string, port string) error {

	staticPath := filepath.Join(directory, staticSubFolder)

	if _, err := os.Stat(staticPath); os.IsNotExist(err) {
		return errors.New(staticPath + " does not exist")
	}

	fs := http.FileServer(http.Dir(staticPath))

	infos, err := ioutil.ReadDir(staticPath)

	if err != nil {
		return errors.New("Couldn't enumerate items in serving path")
	}

	//Install specific handlers for each existing file or directory in the
	//path to serve.
	for _, info := range infos {
		if info.Name() == "index.html" {
			continue
		}
		name := "/" + info.Name()

		if info.IsDir() {
			name += "/"
		} else {

			//Need to check if the file is a symlink to a directory, and symnlinks to directory
			//don't report as a directory in info.
			resolvedPath, err := filepath.EvalSymlinks(filepath.Join(staticPath, info.Name()))
			if err == nil {
				if info, err := os.Stat(resolvedPath); err == nil {
					if info.IsDir() {
						name += "/"
					}
				}
			}
		}

		http.Handle(name, http.StripPrefix(name, fs))
	}

	//This pattern will match as fallback (it's the shortest), and should
	//return "index.html" for everythign that doesn't match one of the ones
	//already returned.
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		//Safe to use since "index.html" is not provided by user but is a constant
		http.ServeFile(w, r, filepath.Join(staticPath, "index.html"))
	})

	return http.ListenAndServe(":"+port, nil)

}

//Static creates a folder of static resources for serving within the static
//subfolder of directory. It symlinks necessary resources in. The return value
//is the directory where the assets can be served from, and an error if there
//was an error. You can clean up the created folder structure with CleanStatic.
func Static(directory string, managers []string, c *config.Config) (assetRoot string, err error) {

	if err := ensureBowerComponents(); err != nil {
		return "", errors.New("bower_components couldn't be created: " + err.Error())
	}

	if _, err := os.Stat(directory); os.IsNotExist(err) {
		return "", errors.New(directory + " did not already exist.")
	}

	staticDir := filepath.Join(directory, staticSubFolder)

	if _, err := os.Stat(staticDir); os.IsNotExist(err) {
		if err := os.Mkdir(staticDir, 0700); err != nil {
			return "", errors.New("Couldn't create static directory: " + err.Error())
		}
	}

	fullPkgPath, err := path.AbsoluteGoPkgPath(staticServerPackage)

	if err != nil {
		return "", errors.New("Couldn't get full package path: " + err.Error())
	}

	//TODO: some of the config files should be copied not symlinked; some of
	//these folders will stay around. Maybe take a temp parameter about
	//whehter it should do copying or not.

	workingDirectory, err := os.Getwd()

	if err != nil {
		return "", errors.New("Can't get working directory: " + err.Error())
	}

	for _, name := range filesToLink {
		localPath := filepath.Join(staticDir, name)
		absLocalDirPath := filepath.Join(workingDirectory, staticDir) + string(filepath.Separator)
		absRemotePath := filepath.Join(fullPkgPath, name)

		relRemotePath, err := path.RelativizePaths(absLocalDirPath, absRemotePath)

		rejoinedPath := filepath.Join(absLocalDirPath, relRemotePath)

		if _, err := os.Stat(rejoinedPath); os.IsNotExist(err) {

			if strings.Contains(name, "bower") {
				return "", errors.New("bower_components doesn't appear to exist. You may need to run `bower update` from within `boardgame/server/static/webapp`.")
			}

			return "", errors.New("Unexpected error: relRemotePath of " + relRemotePath + " doesn't exist " + absLocalDirPath + " : " + absRemotePath + "(" + rejoinedPath + ")")
		}

		if err != nil {
			return "", errors.New("Couldn't relativize paths: " + err.Error())
		}

		if _, err := os.Stat(localPath); err == nil {
			//Must already exist, so can skip
			continue
		}
		fmt.Println("Linking " + localPath + " to " + relRemotePath)
		if err := os.Symlink(relRemotePath, localPath); err != nil {
			return "", errors.New("Couldn't link " + name + ": " + err.Error())
		}

	}

	fmt.Println("Creating " + configJsFileName)
	if err := createConfigJs(filepath.Join(staticDir, configJsFileName), c); err != nil {
		return "", errors.New("Couldn't create " + configJsFileName + ": " + err.Error())
	}

	fmt.Println("Creating " + gameSrcSubFolder)
	if err := linkGameClientFolders(staticDir, managers); err != nil {
		return "", errors.New("Couldn't create " + gameSrcSubFolder + ": " + err.Error())
	}

	return staticDir, nil

}

//ensureBowerComoonents ensures that
//`$GOPATH/src/github.com/jkomoros/boardgame/server/static/webapp` has bower
//components.
func ensureBowerComponents() error {

	p, err := path.AbsoluteGoPkgPath(staticServerPackage)

	if err != nil {
		return err
	}

	if _, err := os.Stat(filepath.Join(p, "bower_components")); err == nil {
		//It appears to exist, we're fine!
		return nil
	}

	_, err = exec.LookPath("bower")

	if err != nil {
		return errors.New("bower_components didn't exist and bower didn't appear to be installed. You need to install bower.")
	}

	cmd := exec.Command("bower", "update")
	cmd.Dir = p
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	fmt.Println("bower_components didn't exist, running `bower update`...")
	if err := cmd.Run(); err != nil {
		return errors.New("Couldn't `bower update`: " + err.Error())
	}

	return nil

}

//linkGameClientFolders creates a game-src within basePath and then links the
//client folders for each one.
func linkGameClientFolders(basePath string, managers []string) error {

	if _, err := os.Stat(basePath); os.IsNotExist(err) {
		return errors.New(basePath + " doesn't exist")
	}

	gameSrcDir := filepath.Join(basePath, gameSrcSubFolder)

	if _, err := os.Stat(gameSrcDir); os.IsNotExist(err) {
		if err := os.Mkdir(gameSrcDir, 0700); err != nil {
			return errors.New("Couldn't create game-src directory: " + err.Error())
		}
	}

	workingDirectory, err := os.Getwd()

	if err != nil {
		return errors.New("Can't get working directory: " + err.Error())
	}

	for _, manager := range managers {
		absPkgPath, err := path.AbsoluteGoPkgPath(manager)

		if err != nil {
			return errors.New("Couldn't generate absPkgPath for " + manager + ": " + err.Error())
		}

		pkgShortName := filepath.Base(manager)

		absClientPath := filepath.Join(absPkgPath, clientSubFolder, pkgShortName)

		if _, err := os.Stat(absClientPath); os.IsNotExist(err) {
			fmt.Println("Skipping " + manager + " because it doesn't appear to have a client sub-directory")
			continue
		}

		relLocalPath := filepath.Join(gameSrcDir, pkgShortName)

		//This feels like it should be relLocalPath, but it needs to be
		//gameSrcDir, otherwise there's an extra ".." in the path. Not really
		//sure why. :-/
		absLocalPath := filepath.Join(workingDirectory, gameSrcDir)

		relPath, err := path.RelativizePaths(absLocalPath, absClientPath)

		if err != nil {
			return errors.New("Couldn't relativize path: " + err.Error())
		}

		rejoinedPath := filepath.Join(absLocalPath, relPath)

		if _, err := os.Stat(rejoinedPath); os.IsNotExist(err) {
			return errors.New("Unexpected error: relPath of " + relPath + " doesn't exist " + absLocalPath + " : " + absClientPath + "(" + rejoinedPath + ")")
		}

		if _, err := os.Stat(relLocalPath); err == nil {
			//Must already exist, so can skip
			continue
		}

		fmt.Println("Linking " + relLocalPath + " to " + relPath)
		if err := os.Symlink(relPath, relLocalPath); err != nil {
			return errors.New("Couldn't create sym lnk for " + manager + ": " + relPath + ":: " + relLocalPath)
		}

	}

	return nil

}

func createConfigJs(path string, c *config.Config) error {
	client := c.Client(false)

	clientBlob, err := json.MarshalIndent(client, "", "\t")

	if err != nil {
		return errors.New("Couldn't create blob: " + err.Error())
	}

	fileContents := "var CONFIG = " + string(clientBlob)

	if err := ioutil.WriteFile(path, []byte(fileContents), 0644); err != nil {
		return errors.New("Couldn't create file: " + err.Error())
	}

	return nil

}

//CleanStatic removes all of the things created in the static subfolder within
//directory.
func CleanStatic(directory string) error {
	return os.RemoveAll(filepath.Join(directory, staticSubFolder))
}
<|endoftext|>


<commit_before>package watcher

import (
	"os"

	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/receptor/serialization"
	"github.com/cloudfoundry-incubator/route-emitter/nats_emitter"
	"github.com/cloudfoundry-incubator/route-emitter/routing_table"
	"github.com/cloudfoundry-incubator/runtime-schema/metric"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/pivotal-golang/lager"
)

var (
	routesRegistered   = metric.Counter("RoutesRegistered")
	routesUnregistered = metric.Counter("RoutesUnregistered")
)

type Watcher struct {
	receptorClient receptor.Client
	table          routing_table.RoutingTable
	emitter        nats_emitter.NATSEmitterInterface
	logger         lager.Logger
}

func NewWatcher(
	receptorClient receptor.Client,
	table routing_table.RoutingTable,
	emitter nats_emitter.NATSEmitterInterface,
	logger lager.Logger,
) *Watcher {
	return &Watcher{
		receptorClient: receptorClient,
		table:          table,
		emitter:        emitter,
		logger:         logger.Session("watcher"),
	}
}

func (watcher *Watcher) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	watcher.logger.Info("starting")

	eventSource, err := watcher.receptorClient.SubscribeToEvents()
	if err != nil {
		watcher.logger.Error("failed-subscribing-to-events", err)
		return err
	}

	close(ready)
	watcher.logger.Info("started")
	defer watcher.logger.Info("finished")

	eventChan := make(chan receptor.Event)
	errChan := make(chan error)
	resubscribeErrChan := make(chan error)

	for {
		go func() {
			if eventSource == nil {
				var resubscribeErr error
				eventSource, resubscribeErr = watcher.receptorClient.SubscribeToEvents()
				if resubscribeErr != nil {
					resubscribeErrChan <- resubscribeErr
					return
				}
			}

			event, err := eventSource.Next()

			if err != nil {
				errChan <- err
			} else if event != nil {
				eventChan <- event
			}
		}()

		select {
		case resubscribeErr := <-resubscribeErrChan:
			watcher.logger.Error("failed-resubscribing-to-events", resubscribeErr)
			if eventSource != nil {
				err := eventSource.Close()
				if err != nil {
					watcher.logger.Error("failed-closing-event-source", err)
				}
			}
			return resubscribeErr

		case event := <-eventChan:
			watcher.logger.Info("handling-event", lager.Data{
				"type": event.EventType(),
			})

			watcher.handleEvent(event)

		case err := <-errChan:
			watcher.logger.Error("failed-getting-next-event", err)
			eventSource = nil

		case <-signals:
			watcher.logger.Info("stopping")
			if eventSource != nil {
				err := eventSource.Close()
				if err != nil {
					watcher.logger.Error("failed-closing-event-source", err)
				}
			}
			return nil
		}
	}

	return nil
}

func (watcher *Watcher) handleEvent(event receptor.Event) {
	switch event := event.(type) {
	case receptor.DesiredLRPCreatedEvent:
		watcher.handleDesiredCreateOrUpdate(serialization.DesiredLRPFromResponse(event.DesiredLRPResponse))
	case receptor.DesiredLRPChangedEvent:
		watcher.handleDesiredCreateOrUpdate(serialization.DesiredLRPFromResponse(event.After))
	case receptor.DesiredLRPRemovedEvent:
		watcher.handleDesiredDelete(serialization.DesiredLRPFromResponse(event.DesiredLRPResponse))
	case receptor.ActualLRPCreatedEvent:
		watcher.handleActualCreate(serialization.ActualLRPFromResponse(event.ActualLRPResponse))
	case receptor.ActualLRPChangedEvent:
		watcher.handleActualUpdate(serialization.ActualLRPFromResponse(event.Before), serialization.ActualLRPFromResponse(event.After))
	case receptor.ActualLRPRemovedEvent:
		watcher.handleActualDelete(serialization.ActualLRPFromResponse(event.ActualLRPResponse))
	default:
		watcher.logger.Info("did-not-handle-unrecognizable-event", lager.Data{"event-type": event.EventType()})
	}
}

func (watcher *Watcher) handleDesiredCreateOrUpdate(desiredLRP models.DesiredLRP) {
	watcher.logger.Debug("handling-desired-create-or-update", desiredLRPData(desiredLRP))
	defer watcher.logger.Debug("done-handling-desired-create-or-update")

	messagesToEmit := watcher.table.SetRoutes(desiredLRP.ProcessGuid, routing_table.Routes{
		URIs:    desiredLRP.Routes,
		LogGuid: desiredLRP.LogGuid,
	})

	watcher.emitter.Emit(messagesToEmit, &routesRegistered, &routesUnregistered)
}

func (watcher *Watcher) handleDesiredDelete(desiredLRP models.DesiredLRP) {
	watcher.logger.Debug("handling-desired-delete", desiredLRPData(desiredLRP))
	defer watcher.logger.Debug("done-handling-desired-delete")

	messagesToEmit := watcher.table.RemoveRoutes(desiredLRP.ProcessGuid)

	watcher.emitter.Emit(messagesToEmit, &routesRegistered, &routesUnregistered)
}

func (watcher *Watcher) handleActualCreate(actualLRP models.ActualLRP) {
	watcher.logger.Debug("handling-actual-create", actualLRPData(actualLRP))
	defer watcher.logger.Debug("done-handling-actual-create")

	if actualLRP.State == models.ActualLRPStateRunning {
		watcher.addOrUpdateAndEmit(actualLRP)
	} else {
		watcher.removeAndEmit(actualLRP)
	}
}

func (watcher *Watcher) handleActualUpdate(before, after models.ActualLRP) {
	watcher.logger.Debug("handling-actual-update", lager.Data{"before": actualLRPData(before), "after": actualLRPData(after)})
	defer watcher.logger.Debug("done-handling-actual-update")

	switch {
	case after.State == models.ActualLRPStateRunning:
		watcher.addOrUpdateAndEmit(after)
	case after.State != models.ActualLRPStateRunning && before.State == models.ActualLRPStateRunning:
		watcher.removeAndEmit(before)
	}
}

func (watcher *Watcher) handleActualDelete(actualLRP models.ActualLRP) {
	watcher.logger.Debug("handling-actual-delete", actualLRPData(actualLRP))
	defer watcher.logger.Debug("done-handling-actual-delete")

	watcher.removeAndEmit(actualLRP)
}

func (watcher *Watcher) addOrUpdateAndEmit(actualLRP models.ActualLRP) {
	container, err := routing_table.ContainerFromActual(actualLRP)
	if err != nil {
		watcher.logger.Error("failed-to-extract-container-from-actual", err)
		return
	}

	messagesToEmit := watcher.table.AddOrUpdateContainer(actualLRP.ProcessGuid, container)
	watcher.emitter.Emit(messagesToEmit, &routesRegistered, &routesUnregistered)
}

func (watcher *Watcher) removeAndEmit(actualLRP models.ActualLRP) {
	container, err := routing_table.ContainerFromActual(actualLRP)
	if err != nil {
		watcher.logger.Error("failed-to-extract-container-from-actual", err)
		return
	}

	messagesToEmit := watcher.table.RemoveContainer(actualLRP.ProcessGuid, container)
	watcher.emitter.Emit(messagesToEmit, &routesRegistered, &routesUnregistered)
}

func desiredLRPData(lrp models.DesiredLRP) lager.Data {
	return lager.Data{
		"process-guid": lrp.ProcessGuid,
		"routes":       lrp.Routes,
		"ports":        lrp.Ports,
	}
}

func actualLRPData(lrp models.ActualLRP) lager.Data {
	return lager.Data{
		"process-guid":  lrp.ActualLRPKey.ProcessGuid,
		"index":         lrp.ActualLRPKey.Index,
		"container-key": lrp.ActualLRPContainerKey,
		"net-info":      lrp.ActualLRPNetInfo,
	}
}
<commit_msg>remove vigilante else branch causing bad logs<commit_after>package watcher

import (
	"os"

	"github.com/cloudfoundry-incubator/receptor"
	"github.com/cloudfoundry-incubator/receptor/serialization"
	"github.com/cloudfoundry-incubator/route-emitter/nats_emitter"
	"github.com/cloudfoundry-incubator/route-emitter/routing_table"
	"github.com/cloudfoundry-incubator/runtime-schema/metric"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/pivotal-golang/lager"
)

var (
	routesRegistered   = metric.Counter("RoutesRegistered")
	routesUnregistered = metric.Counter("RoutesUnregistered")
)

type Watcher struct {
	receptorClient receptor.Client
	table          routing_table.RoutingTable
	emitter        nats_emitter.NATSEmitterInterface
	logger         lager.Logger
}

func NewWatcher(
	receptorClient receptor.Client,
	table routing_table.RoutingTable,
	emitter nats_emitter.NATSEmitterInterface,
	logger lager.Logger,
) *Watcher {
	return &Watcher{
		receptorClient: receptorClient,
		table:          table,
		emitter:        emitter,
		logger:         logger.Session("watcher"),
	}
}

func (watcher *Watcher) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	watcher.logger.Info("starting")

	eventSource, err := watcher.receptorClient.SubscribeToEvents()
	if err != nil {
		watcher.logger.Error("failed-subscribing-to-events", err)
		return err
	}

	close(ready)
	watcher.logger.Info("started")
	defer watcher.logger.Info("finished")

	eventChan := make(chan receptor.Event)
	errChan := make(chan error)
	resubscribeErrChan := make(chan error)

	for {
		go func() {
			if eventSource == nil {
				var resubscribeErr error
				eventSource, resubscribeErr = watcher.receptorClient.SubscribeToEvents()
				if resubscribeErr != nil {
					resubscribeErrChan <- resubscribeErr
					return
				}
			}

			event, err := eventSource.Next()

			if err != nil {
				errChan <- err
			} else if event != nil {
				eventChan <- event
			}
		}()

		select {
		case resubscribeErr := <-resubscribeErrChan:
			watcher.logger.Error("failed-resubscribing-to-events", resubscribeErr)
			if eventSource != nil {
				err := eventSource.Close()
				if err != nil {
					watcher.logger.Error("failed-closing-event-source", err)
				}
			}
			return resubscribeErr

		case event := <-eventChan:
			watcher.logger.Info("handling-event", lager.Data{
				"type": event.EventType(),
			})

			watcher.handleEvent(event)

		case err := <-errChan:
			watcher.logger.Error("failed-getting-next-event", err)
			eventSource = nil

		case <-signals:
			watcher.logger.Info("stopping")
			if eventSource != nil {
				err := eventSource.Close()
				if err != nil {
					watcher.logger.Error("failed-closing-event-source", err)
				}
			}
			return nil
		}
	}

	return nil
}

func (watcher *Watcher) handleEvent(event receptor.Event) {
	switch event := event.(type) {
	case receptor.DesiredLRPCreatedEvent:
		watcher.handleDesiredCreateOrUpdate(serialization.DesiredLRPFromResponse(event.DesiredLRPResponse))
	case receptor.DesiredLRPChangedEvent:
		watcher.handleDesiredCreateOrUpdate(serialization.DesiredLRPFromResponse(event.After))
	case receptor.DesiredLRPRemovedEvent:
		watcher.handleDesiredDelete(serialization.DesiredLRPFromResponse(event.DesiredLRPResponse))
	case receptor.ActualLRPCreatedEvent:
		watcher.handleActualCreate(serialization.ActualLRPFromResponse(event.ActualLRPResponse))
	case receptor.ActualLRPChangedEvent:
		watcher.handleActualUpdate(serialization.ActualLRPFromResponse(event.Before), serialization.ActualLRPFromResponse(event.After))
	case receptor.ActualLRPRemovedEvent:
		watcher.handleActualDelete(serialization.ActualLRPFromResponse(event.ActualLRPResponse))
	default:
		watcher.logger.Info("did-not-handle-unrecognizable-event", lager.Data{"event-type": event.EventType()})
	}
}

func (watcher *Watcher) handleDesiredCreateOrUpdate(desiredLRP models.DesiredLRP) {
	watcher.logger.Debug("handling-desired-create-or-update", desiredLRPData(desiredLRP))
	defer watcher.logger.Debug("done-handling-desired-create-or-update")

	messagesToEmit := watcher.table.SetRoutes(desiredLRP.ProcessGuid, routing_table.Routes{
		URIs:    desiredLRP.Routes,
		LogGuid: desiredLRP.LogGuid,
	})

	watcher.emitter.Emit(messagesToEmit, &routesRegistered, &routesUnregistered)
}

func (watcher *Watcher) handleDesiredDelete(desiredLRP models.DesiredLRP) {
	watcher.logger.Debug("handling-desired-delete", desiredLRPData(desiredLRP))
	defer watcher.logger.Debug("done-handling-desired-delete")

	messagesToEmit := watcher.table.RemoveRoutes(desiredLRP.ProcessGuid)

	watcher.emitter.Emit(messagesToEmit, &routesRegistered, &routesUnregistered)
}

func (watcher *Watcher) handleActualCreate(actualLRP models.ActualLRP) {
	watcher.logger.Debug("handling-actual-create", actualLRPData(actualLRP))
	defer watcher.logger.Debug("done-handling-actual-create")

	if actualLRP.State == models.ActualLRPStateRunning {
		watcher.addOrUpdateAndEmit(actualLRP)
	}
}

func (watcher *Watcher) handleActualUpdate(before, after models.ActualLRP) {
	watcher.logger.Debug("handling-actual-update", lager.Data{"before": actualLRPData(before), "after": actualLRPData(after)})
	defer watcher.logger.Debug("done-handling-actual-update")

	switch {
	case after.State == models.ActualLRPStateRunning:
		watcher.addOrUpdateAndEmit(after)
	case after.State != models.ActualLRPStateRunning && before.State == models.ActualLRPStateRunning:
		watcher.removeAndEmit(before)
	}
}

func (watcher *Watcher) handleActualDelete(actualLRP models.ActualLRP) {
	watcher.logger.Debug("handling-actual-delete", actualLRPData(actualLRP))
	defer watcher.logger.Debug("done-handling-actual-delete")

	watcher.removeAndEmit(actualLRP)
}

func (watcher *Watcher) addOrUpdateAndEmit(actualLRP models.ActualLRP) {
	container, err := routing_table.ContainerFromActual(actualLRP)
	if err != nil {
		watcher.logger.Error("failed-to-extract-container-from-actual", err)
		return
	}

	messagesToEmit := watcher.table.AddOrUpdateContainer(actualLRP.ProcessGuid, container)
	watcher.emitter.Emit(messagesToEmit, &routesRegistered, &routesUnregistered)
}

func (watcher *Watcher) removeAndEmit(actualLRP models.ActualLRP) {
	container, err := routing_table.ContainerFromActual(actualLRP)
	if err != nil {
		watcher.logger.Error("failed-to-extract-container-from-actual", err)
		return
	}

	messagesToEmit := watcher.table.RemoveContainer(actualLRP.ProcessGuid, container)
	watcher.emitter.Emit(messagesToEmit, &routesRegistered, &routesUnregistered)
}

func desiredLRPData(lrp models.DesiredLRP) lager.Data {
	return lager.Data{
		"process-guid": lrp.ProcessGuid,
		"routes":       lrp.Routes,
		"ports":        lrp.Ports,
	}
}

func actualLRPData(lrp models.ActualLRP) lager.Data {
	return lager.Data{
		"process-guid":  lrp.ActualLRPKey.ProcessGuid,
		"index":         lrp.ActualLRPKey.Index,
		"container-key": lrp.ActualLRPContainerKey,
		"net-info":      lrp.ActualLRPNetInfo,
	}
}
<|endoftext|>


<commit_before>package gtk

// #include <gtk/gtk.h>
// #include "gtk.go.h"
import "C"
import (
	"unsafe"

	"github.com/gotk3/gotk3/glib"
)

func init() {
	tm := []glib.TypeMarshaler{
		{glib.Type(C.gtk_info_bar_get_type()), marshalInfoBar},
	}

	glib.RegisterGValueMarshalers(tm)

	WrapMap["GtkInfoBar"] = wrapInfoBar
}

type InfoBar struct {
	Box
}

func (v *InfoBar) native() *C.GtkInfoBar {
	if v == nil || v.GObject == nil {
		return nil
	}

	p := unsafe.Pointer(v.GObject)
	return C.toGtkInfoBar(p)
}

func marshalInfoBar(p uintptr) (interface{}, error) {
	c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
	return wrapInfoBar(wrapObject(unsafe.Pointer(c))), nil
}

func wrapInfoBar(obj *glib.Object) *InfoBar {
	return &InfoBar{Box{Container{Widget{glib.InitiallyUnowned{obj}}}}}
}
<commit_msg>Add InfoBar methods<commit_after>package gtk

// #include <gtk/gtk.h>
// #include "gtk.go.h"
import "C"
import (
	"unsafe"

	"github.com/gotk3/gotk3/glib"
)

func init() {
	tm := []glib.TypeMarshaler{
		{glib.Type(C.gtk_info_bar_get_type()), marshalInfoBar},
	}

	glib.RegisterGValueMarshalers(tm)

	WrapMap["GtkInfoBar"] = wrapInfoBar
}

type InfoBar struct {
	Box
}

func (v *InfoBar) native() *C.GtkInfoBar {
	if v == nil || v.GObject == nil {
		return nil
	}

	p := unsafe.Pointer(v.GObject)
	return C.toGtkInfoBar(p)
}

func marshalInfoBar(p uintptr) (interface{}, error) {
	c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
	return wrapInfoBar(wrapObject(unsafe.Pointer(c))), nil
}

func wrapInfoBar(obj *glib.Object) *InfoBar {
	return &InfoBar{Box{Container{Widget{glib.InitiallyUnowned{obj}}}}}
}

func InfoBarNew() (*InfoBar, error) {
	c := C.gtk_info_bar_new()
	if c == nil {
		return nil, nilPtrErr
	}

	return wrapInfoBar(wrapObject(unsafe.Pointer(c))), nil
}

func (v *InfoBar) AddActionWidget(w IWidget, responseId ResponseType) {
	C.gtk_info_bar_add_action_widget(v.native(), w.toWidget(), C.gint(responseId))
}

func (v *InfoBar) AddButton(buttonText string, responseId ResponseType) {
	cstr := C.CString(buttonText)
	defer C.free(unsafe.Pointer(cstr))

	C.gtk_info_bar_add_button(v.native(), (*C.gchar)(cstr), C.gint(responseId))
}

func (v *InfoBar) SetResponseSensitive(responseId ResponseType, setting bool) {
	C.gtk_info_bar_set_response_sensitive(v.native(), C.gint(responseId), gbool(setting))
}

func (v *InfoBar) SetDefaultResponse(responseId ResponseType) {
	C.gtk_info_bar_set_default_response(v.native(), C.gint(responseId))
}

func (v *InfoBar) SetMessageType(messageType MessageType) {
	C.gtk_info_bar_set_message_type(v.native(), C.GtkMessageType(messageType))
}

func (v *InfoBar) GetMessageType() MessageType {
	messageType := C.gtk_info_bar_get_message_type(v.native())
	return MessageType(messageType)
}

func (v *InfoBar) GetActionArea() (*Widget, error) {
	c := C.gtk_info_bar_get_action_area(v.native())
	if c == nil {
		return nil, nilPtrErr
	}

	return wrapWidget(wrapObject(unsafe.Pointer(c))), nil
}

func (v *InfoBar) GetContentArea() (*Widget, error) {
	c := C.gtk_info_bar_get_content_area(v.native())
	if c == nil {
		return nil, nilPtrErr
	}

	return wrapWidget(wrapObject(unsafe.Pointer(c))), nil
}

func (v *InfoBar) GetShowCloseButton() bool {
	b := C.gtk_info_bar_get_show_close_button(v.native())
	return gobool(b)
}

func (v *InfoBar) SetShowCloseButton(setting bool) {
	C.gtk_info_bar_set_show_close_button(v.native(), gbool(setting))
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gxml_test

import (
	"bytes"
	"github.com/gogf/gf/g/encoding/gcharset"
	"github.com/gogf/gf/g/encoding/gparser"
	"github.com/gogf/gf/g/encoding/gxml"
	"strings"
	"testing"
)

var testData = []struct {
	utf8, other, otherEncoding string
}{
	{"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"},
	{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
	{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"},
}

var testErrData = []struct {
	utf8, other, otherEncoding string
}{
	{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
}

func buildXml(charset string, str string) (string, string) {
	head := `<?xml version="1.0" encoding="UTF-8"?>`
	srcXml := strings.Replace(head, "UTF-8", charset, -1)

	srcParser := gparser.New(nil)
	srcParser.Set("name", str)
	srcParser.Set("age", "12")

	s, err := srcParser.ToXml()
	if err != nil {
		return "", ""
	}

	srcXml = srcXml + string(s)
	srcXml, err = gcharset.UTF8To(charset, srcXml)
	if err != nil {
		return "", ""
	}

	dstXml := head + string(s)

	return srcXml, dstXml
}

//测试XML中字符集的转换
func Test_XmlToJson(t *testing.T) {
	for _, v := range testData {
		srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
		if len(srcXml) == 0 && len(dstXml) == 0 {
			t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
		}

		srcJson, err := gxml.ToJson([]byte(srcXml))
		if err != nil {
			t.Errorf("gxml.ToJson error. %s", srcXml)
		}

		dstJson, err := gxml.ToJson([]byte(dstXml))
		if err != nil {
			t.Errorf("dstXml to json error. %s", dstXml)
		}

		if bytes.Compare(srcJson, dstJson) != 0 {
			t.Errorf("convert to json error. srcJson:%s, dstJson:%s", string(srcJson), string(dstJson))
		}

	}
}

func Test_Decode(t *testing.T) {
	for _, v := range testData {
		srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
		if len(srcXml) == 0 && len(dstXml) == 0 {
			t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
		}

		srcMap, err := gxml.Decode([]byte(srcXml))
		if err != nil {
			t.Errorf("gxml.Decode error. %s", srcXml)
		}

		dstMap, err := gxml.Decode([]byte(dstXml))
		if err != nil {
			t.Errorf("gxml decode error. %s", dstXml)
		}
		s := srcMap["doc"].(map[string]interface{})
		d := dstMap["doc"].(map[string]interface{})
		for kk, vv := range s {
			if vv.(string) != d[kk].(string) {
				t.Errorf("convert to map error. src:%v, dst:%v", vv, d[kk])
			}
		}
	}
}

func Test_Encode(t *testing.T) {
	m := make(map[string]interface{})
	v := map[string]interface{}{
		"string": "hello world",
		"int":    123,
		"float":  100.92,
		"bool":   true,
	}
	m["root"] = interface{}(v)

	xmlStr, err := gxml.Encode(m)
	if err != nil {
		t.Errorf("encode error.")
	}
	//t.Logf("%s\n", string(xmlStr))

	res := `<root><bool>true</bool><float>100.92</float><int>123</int><string>hello world</string></root>`
	if string(xmlStr) != res {
		t.Errorf("encode error. result: [%s], expect:[%s]", string(xmlStr), res)
	}
}

func Test_EncodeIndent(t *testing.T) {
	m := make(map[string]interface{})
	v := map[string]interface{}{
		"string": "hello world",
		"int":    123,
		"float":  100.92,
		"bool":   true,
	}
	m["root"] = interface{}(v)

	_, err := gxml.EncodeWithIndent(m, "xml")
	if err != nil {
		t.Errorf("encodeWithIndent error.")
	}

	//t.Logf("%s\n", string(xmlStr))

}

func TestErrXml(t *testing.T) {
	for _, v := range testErrData {
		srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
		if len(srcXml) == 0 && len(dstXml) == 0 {
			t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
		}

		srcXml = strings.Replace(srcXml, "gbk", "XXX", -1)
		_, err := gxml.ToJson([]byte(srcXml))
		if err == nil {
			t.Errorf("srcXml to json should be failed. %s", srcXml)
		}

	}
}
<commit_msg>框架中增加字符集转换的标准库<commit_after>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gxml_test

import (
	"bytes"
	"github.com/gogf/gf/g/encoding/gcharset"
	"github.com/gogf/gf/g/encoding/gparser"
	"github.com/gogf/gf/g/encoding/gxml"
	"github.com/gogf/gf/g/test/gtest"
	"strings"
	"testing"
)

var testData = []struct {
	utf8, other, otherEncoding string
}{
	{"Hello 常用國字標準字體表", "Hello \xb1`\xa5\u03b0\xea\xa6r\xbc\u0437\u01e6r\xc5\xe9\xaa\xed", "big5"},
	{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
	{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gb18030"},
}

var testErrData = []struct {
	utf8, other, otherEncoding string
}{
	{"Hello 常用國字標準字體表", "Hello \xb3\xa3\xd3\xc3\x87\xf8\xd7\xd6\x98\xcb\x9c\xca\xd7\xd6\xf3\x77\xb1\xed", "gbk"},
}

func buildXml(charset string, str string) (string, string) {
	head := `<?xml version="1.0" encoding="UTF-8"?>`
	srcXml := strings.Replace(head, "UTF-8", charset, -1)

	srcParser := gparser.New(nil)
	srcParser.Set("name", str)
	srcParser.Set("age", "12")

	s, err := srcParser.ToXml()
	if err != nil {
		return "", ""
	}

	srcXml = srcXml + string(s)
	srcXml, err = gcharset.UTF8To(charset, srcXml)
	if err != nil {
		return "", ""
	}

	dstXml := head + string(s)

	return srcXml, dstXml
}

//测试XML中字符集的转换
func Test_XmlToJson(t *testing.T) {
	for _, v := range testData {
		srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
		if len(srcXml) == 0 && len(dstXml) == 0 {
			t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
		}

		srcJson, err := gxml.ToJson([]byte(srcXml))
		if err != nil {
			t.Errorf("gxml.ToJson error. %s", srcXml)
		}

		dstJson, err := gxml.ToJson([]byte(dstXml))
		if err != nil {
			t.Errorf("dstXml to json error. %s", dstXml)
		}

		if bytes.Compare(srcJson, dstJson) != 0 {
			t.Errorf("convert to json error. srcJson:%s, dstJson:%s", string(srcJson), string(dstJson))
		}

	}
}

func Test_Decode(t *testing.T) {
	for _, v := range testData {
		srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
		if len(srcXml) == 0 && len(dstXml) == 0 {
			t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
		}

		srcMap, err := gxml.Decode([]byte(srcXml))
		if err != nil {
			t.Errorf("gxml.Decode error. %s", srcXml)
		}

		dstMap, err := gxml.Decode([]byte(dstXml))
		if err != nil {
			t.Errorf("gxml decode error. %s", dstXml)
		}
		s := srcMap["doc"].(map[string]interface{})
		d := dstMap["doc"].(map[string]interface{})
		for kk, vv := range s {
			if vv.(string) != d[kk].(string) {
				t.Errorf("convert to map error. src:%v, dst:%v", vv, d[kk])
			}
		}
	}
}

func Test_Encode(t *testing.T) {
	m := make(map[string]interface{})
	v := map[string]interface{}{
		"string": "hello world",
		"int":    123,
		"float":  100.92,
		"bool":   true,
	}
	m["root"] = interface{}(v)

	xmlStr, err := gxml.Encode(m)
	if err != nil {
		t.Errorf("encode error.")
	}
	//t.Logf("%s\n", string(xmlStr))

	res := `<root><bool>true</bool><float>100.92</float><int>123</int><string>hello world</string></root>`
	if string(xmlStr) != res {
		t.Errorf("encode error. result: [%s], expect:[%s]", string(xmlStr), res)
	}
}

func Test_EncodeIndent(t *testing.T) {
	m := make(map[string]interface{})
	v := map[string]interface{}{
		"string": "hello world",
		"int":    123,
		"float":  100.92,
		"bool":   true,
	}
	m["root"] = interface{}(v)

	_, err := gxml.EncodeWithIndent(m, "xml")
	if err != nil {
		t.Errorf("encodeWithIndent error.")
	}

	//t.Logf("%s\n", string(xmlStr))

}

func TestErrXml(t *testing.T) {
	for _, v := range testErrData {
		srcXml, dstXml := buildXml(v.otherEncoding, v.utf8)
		if len(srcXml) == 0 && len(dstXml) == 0 {
			t.Errorf("build xml string error. srcEncoding:%s, src:%s, utf8:%s", v.otherEncoding, v.other, v.utf8)
		}

		srcXml = strings.Replace(srcXml, "gbk", "XXX", -1)
		_, err := gxml.ToJson([]byte(srcXml))
		if err == nil {
			t.Errorf("srcXml to json should be failed. %s", srcXml)
		}

	}
}

func TestErrCase(t *testing.T) {
	gtest.Case(t, func() {
		errXml := `<root><bool>true</bool><float>100.92</float><int>123</int><string>hello world</string>`
		_, err := gxml.ToJson([]byte(errXml))
		if err == nil {
			t.Errorf("unexpected value: nil")
		}
	})

	gtest.Case(t, func() {
		errXml := `<root><bool>true</bool><float>100.92</float><int>123</int><string>hello world</string>`
		_, err := gxml.Decode([]byte(errXml))
		if err == nil {
			t.Errorf("unexpected value: nil")
		}
	})
}
<|endoftext|>


<commit_before>package dao

import "labix.org/v2/mgo/bson"

//------------------------------------------------------------
// DAO update array methods
//------------------------------------------------------------

// Adds pushObj element to array pushTo.
func (dao *DAO) Update_ArrayPush(id bson.ObjectId, pushTo string, pushObj interface{}) (err error) {

	q := M{
		"$push": M{pushTo: pushObj},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Adds pushObjs elements to array pushTo.
func (dao *DAO) Update_ArrayPushAll(id bson.ObjectId, pushTo string, pushObjs []interface{}) (err error) {

	q := M{
		"$pushAll": M{pushTo: pushObjs},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Removes pullObj element from pullFrom array.
// Adds pushObj element to pushTo array.
func (dao *DAO) Update_ArraysPullPush(id bson.ObjectId, pullFrom string, pullObj interface{}, pushTo string, pushObj interface{}) (err error) {

	q := M{
		"$pull": M{pullFrom: pullObj},
		"$push": M{pushTo: pushObj},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Removes pullObjs elements from pullFrom array.
// Adds pushObjs elements to pushTo array.
func (dao *DAO) Update_ArraysPullPushAll(id bson.ObjectId, pullFrom string, pullObjs []interface{}, pushTo string, pushObjs []interface{}) (err error) {

	q := M{
		"$pullAll": M{pullFrom: pullObjs},
		"$pushAll": M{pushTo: pushObjs},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Removes pullFrom array element that matches pullObj.
func (dao *DAO) Update_ArrayPull(id bson.ObjectId, pullFrom string, pullObj interface{}) (err error) {

	q := M{
		"$pull": M{pullFrom: pullObj},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Removes pullFrom array elements that matches pullObjs.
func (dao *DAO) Update_ArrayPullAll(id bson.ObjectId, pullFrom string, pullObjs []interface{}) (err error) {

	q := M{
		"$pullAll": M{pullFrom: pullObjs},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}
<commit_msg>added pushMany and pullMany methods<commit_after>package dao

import "labix.org/v2/mgo/bson"

//------------------------------------------------------------
// DAO update array methods
//------------------------------------------------------------

// Adds pushObj element to array pushTo.
func (dao *DAO) Update_ArrayPush(id bson.ObjectId, pushTo string, pushObj interface{}) (err error) {

	q := M{
		"$push": M{pushTo: pushObj},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Adds pushObjs elements to array pushTo.
func (dao *DAO) Update_ArrayPushMany(id bson.ObjectId, pushTo string, pushObjs []interface{}) (err error) {

	q := M{
		"$pushAll": M{pushTo: pushObjs},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Removes pullObj element from pullFrom array.
// Adds pushObj element to pushTo array.
func (dao *DAO) Update_ArraysPullPush(id bson.ObjectId, pullFrom string, pullObj interface{}, pushTo string, pushObj interface{}) (err error) {

	q := M{
		"$pull": M{pullFrom: pullObj},
		"$push": M{pushTo: pushObj},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Removes pullObjs elements from pullFrom array.
// Adds pushObjs elements to pushTo array.
func (dao *DAO) Update_ArraysPullPushMany(id bson.ObjectId, pullFrom string, pullObjs []interface{}, pushTo string, pushObjs []interface{}) (err error) {

	q := M{
		"$pullAll": M{pullFrom: pullObjs},
		"$pushAll": M{pushTo: pushObjs},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Removes pullFrom array element that matches pullObj.
func (dao *DAO) Update_ArrayPull(id bson.ObjectId, pullFrom string, pullObj interface{}) (err error) {

	q := M{
		"$pull": M{pullFrom: pullObj},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}

// Removes pullFrom array elements that matches pullObjs.
func (dao *DAO) Update_ArrayPullMany(id bson.ObjectId, pullFrom string, pullObjs []interface{}) (err error) {

	q := M{
		"$pullAll": M{pullFrom: pullObjs},
	}
	err = dao.Coll.UpdateId(id, q)
	return
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"
	"io"
	"net"
	"net/mail"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/scanner"
	"unicode"

	"github.com/yuuki/gokc/log"
)

const (
	EOF = 0
)

var SYMBOL_TABLES = map[string]int{
	"{": LB,
	"}": RB,

	"global_defs":                     GLOBALDEFS,
	"notification_email":              NOTIFICATION_EMAIL,
	"notification_email_from":         NOTIFICATION_EMAIL_FROM,
	"smtp_server":                     SMTP_SERVER,
	"smtp_connect_timeout":            SMTP_CONNECT_TIMEOUT,
	"router_id":                       ROUTER_ID,
	"lvs_id":                          LVS_ID,
	"vrrp_mcast_group4":               VRRP_MCAST_GROUP4,
	"vrrp_mcast_group6":               VRRP_MCAST_GROUP6,
	"vrrp_garp_master_delay":          VRRP_GARP_MASTER_DELAY,
	"vrrp_garp_master_repeat":         VRRP_GARP_MASTER_REPEAT,
	"vrrp_garp_master_refresh":        VRRP_GARP_MASTER_REFRESH,
	"vrrp_garp_master_refresh_repeat": VRRP_GARP_MASTER_REFRESH_REPEAT,
	"vrrp_version":                    VRRP_VERSION,

	"static_ipaddress": STATIC_IPADDRESS,
	"static_routes":    STATIC_ROUTES,
	"static_rules":     STATIC_RULES,

	"vrrp_sync_group": VRRP_SYNC_GROUP,
	"group":           GROUP,

	"vrrp_instance":              VRRP_INSTANCE,
	"use_vmac":                   USE_VMAC,
	"version":                    VERSION,
	"vmac_xmit_base":             VMAC_XMIT_BASE,
	"native_ipv6":                NATIVE_IPV6,
	"interface":                  INTERFACE,
	"mcast_src_ip":               MCAST_SRC_IP,
	"unicast_src_ip":             UNICAST_SRC_IP,
	"unicast_peer":               UNICAST_PEER,
	"lvs_sync_daemon_interface":  LVS_SYNC_DAEMON_INTERFACE,
	"virtual_router_id":          VIRTUAL_ROUTER_ID,
	"nopreempt":                  NOPREEMPT,
	"priority":                   PRIORITY,
	"advert_int":                 ADVERT_INT,
	"virtual_ipaddress":          VIRTUAL_IPADDRESS,
	"virtual_ipaddress_excluded": VIRTUAL_IPADDRESS_EXCLUDED,
	"virtual_routes":             VIRTUAL_ROUTES,
	"state":                      STATE,
	"MASTER":                     MASTER,
	"BACKUP":                     BACKUP,
	"garp_master_delay":          GARP_MASTER_DELAY,
	"smtp_alert":                 SMTP_ALERT,
	"authentication":             AUTHENTICATION,
	"auth_type":                  AUTH_TYPE,
	"auth_pass":                  AUTH_PASS,
	"PASS":                       PASS,
	"AH":                         AH,
	"label":                      LABEL,
	"dev":                        DEV,
	"scope":                      SCOPE,
	"site":                       SITE,
	"link":                       LINK,
	"host":                       HOST,
	"nowhere":                    NOWHERE,
	"global":                     GLOBAL,
	"brd":                        BRD,
	"src":                        SRC,
	"from":                       FROM,
	"to":                         TO,
	"via":                        VIA,
	"gw":                         GW,
	"or":                         OR,
	"table":                      TABLE,
	"metric":                     METRIC,
	"blackhole":                  BLACKHOLE,
	"track_interface":            TRACK_INTERFACE,
	"track_script":               TRACK_SCRIPT,
	"dont_track_primary":         DONT_TRACK_PRIMARY,
	"notify_master":              NOTIFY_MASTER,
	"notify_backup":              NOTIFY_BACKUP,
	"notify_fault":               NOTIFY_FAULT,
	"notify_stop":                NOTIFY_STOP,
	"notify":                     NOTIFY,

	"vrrp_script": VRRP_SCRIPT,
	"script":      SCRIPT,
	"interval":    INTERVAL,
	"timeout":     TIMEOUT,
	"fall":        FALL,
	"rise":        RISE,

	"virtual_server_group": VIRTUAL_SERVER_GROUP,
	"fwmark":               FWMARK,

	"virtual_server": VIRTUAL_SERVER,
	"delay_loop":     DELAY_LOOP,
	"lb_algo":        LB_ALGO,
	"lb_kind":        LB_KIND,
	"lvs_sched":      LVS_SCHED,
	"lvs_method":     LVS_METHOD,
	"rr":             RR,
	"wrr":            WRR,
	"lc":             LC,
	"wlc":            WLC,
	"fo":             FO,
	"ovf":            OVF,
	"lblc":           LBLC,
	"lblcr":          LBLCR,
	"sh":             SH,
	"dh":             DH,
	"sed":            SED,
	"nq":             NQ,
	"NAT":            NAT,
	"DR":             DR,
	"TUN":            TUN,
	"persistence_timeout": PERSISTENCE_TIMEOUT,
	"protocol":            PROTOCOL,
	"TCP":                 TCP,
	"UDP":                 UDP,
	"sorry_server":        SORRY_SERVER,
	"real_server":         REAL_SERVER,
	"weight":              WEIGHT,
	"inhibit_on_failure":  INHIBIT_ON_FAILURE,
	"TCP_CHECK":           TCP_CHECK,
	"HTTP_GET":            HTTP_GET,
	"SSL_GET":             SSL_GET,
	"SMTP_CHECK":          SMTP_CHECK,
	"DNS_CHECK":           DNS_CHECK,
	"MISC_CHECK":          MISC_CHECK,
	"url":                 URL,
	"path":                PATH,
	"digest":              DIGEST,
	"status_code":         STATUS_CODE,
	"connect_timeout":     CONNECT_TIMEOUT,
	"connect_port":        CONNECT_PORT,
	"connect_ip":          CONNECT_IP,
	"bindto":              BINDTO,
	"bind_port":           BIND_PORT,
	"retry":               RETRY,
	"helo_name":           HELO_NAME,
	"delay_before_retry":  DELAY_BEFORE_RETRY,
	"type":                TYPE,
	"name":                NAME,
	"misc_path":           MISC_PATH,
	"misc_timeout":        MISC_TIMEOUT,
	"warmup":              WARMUP,
	"misc_dynamic":        MISC_DYNAMIC,
	"nb_get_retry":        NB_GET_RETRY,
	"virtualhost":         VIRTUALHOST,
	"alpha":               ALPHA,
	"omega":               OMEGA,
	"quorum":              QUORUM,
	"hysteresis":          HYSTERESIS,
	"quorum_up":           QUORUM_UP,
	"quorum_down":         QUORUM_DOWN,
}

type Tokenizer struct {
	scanner  scanner.Scanner
	filename string
}

func NewTokenizer(src io.Reader, filename string) *Tokenizer {
	var t Tokenizer
	t.scanner.Init(src)
	t.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats | scanner.ScanChars | scanner.ScanRawStrings | scanner.ScanComments | scanner.SkipComments
	t.scanner.IsIdentRune = isIdentRune
	t.filename = filename
	return &t
}

func (t *Tokenizer) NextAll() ([]*Token, error) {
	var result []*Token

	for {
		token, s := t.scanNextToken()

		for s == "include" {
			token, s = t.scanNextToken()

			tokens, err := t.scanInclude(s)
			if err != nil {
				return nil, err
			}
			result = append(result, tokens...)

			token, s = t.scanNextToken()
		}

		if token == scanner.EOF {
			break
		}

		if token == scanner.Ident || token == scanner.String {
			token = STRING
		}

		if _, err := strconv.Atoi(s); err == nil {
			token = NUMBER
		}

		if ip := net.ParseIP(s); ip != nil {
			if ip.To4() != nil {
				token = IPV4
			} else if ip.To16() != nil {
				token = IPV6
			} else {
				log.Infof("warning: %s may be IP address?", s)
			}
		}

		if _, _, err := net.ParseCIDR(s); err == nil {
			token = IP_CIDR
		}

		// IPADDR_RANGE(XXX.YYY.ZZZ.WWW-VVV)
		if ss := strings.Split(s, "-"); len(ss) == 2 {
			if net.ParseIP(ss[0]) != nil {
				if ok, _ := regexp.MatchString(`^[\d]{1,3}$`, ss[1]); ok {
					token = IPADDR_RANGE
				}
			}
		}

		if ok, _ := regexp.MatchString(`^[[:xdigit:]]{32}$`, s); ok {
			token = HEX32
		}

		if ok, _ := regexp.MatchString(`/^([[:alnum:]./-_])*`, s); ok {
			token = PATHSTR
		}

		if _, err := mail.ParseAddress(s); err == nil {
			token = EMAIL
		}

		if _, ok := SYMBOL_TABLES[s]; ok {
			token = SYMBOL_TABLES[s]
		}

		result = append(result, &Token{
			value:    token,
			filename: t.filename,
			line:     t.scanner.Line,
			column:   t.scanner.Column,
		})
	}

	return result, nil
}

func skipComments(scanner *scanner.Scanner) {
	ch := scanner.Next()
	for ch != '\n' && ch >= 0 {
		ch = scanner.Next()
	}
}

func (t *Tokenizer) scanNextToken() (int, string) {
	token := int(t.scanner.Scan())
	s := t.scanner.TokenText()

	for s == "!" || s == "#" {
		skipComments(&t.scanner)

		token = int(t.scanner.Scan())
		s = t.scanner.TokenText()
	}

	log.Debugf("token text: %s\n", s)

	return token, s
}

func (t *Tokenizer) scanInclude(rawfilename string) ([]*Token, error) {
	curDir, err := filepath.Abs(".")
	if err != nil {
		return nil, err
	}

	baseDir := filepath.Dir(t.filename)
	os.Chdir(baseDir)
	defer os.Chdir(curDir)

	rawpaths, err := filepath.Glob(rawfilename)
	if err != nil {
		return nil, err
	}

	if len(rawpaths) < 1 {
		return nil, fmt.Errorf("warning: %s: No such file or directory", rawfilename)
	}

	prevScanner := t.scanner
	defer func() { t.scanner = prevScanner }()
	prevFilename := t.filename
	defer func() { t.filename = prevFilename }()

	var result []*Token
	for _, rawpath := range rawpaths {
		t.filename = rawpath
		log.Verbosef("--> Parsing ... %s\n", rawpath)

		f, err := os.Open(rawpath)
		if err != nil {
			return nil, err
		}

		t.scanner.Init(f)
		t.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats | scanner.ScanChars | scanner.ScanRawStrings | scanner.ScanComments | scanner.SkipComments
		t.scanner.IsIdentRune = isIdentRune
		tokens, err := t.NextAll()
		if err != nil {
			return nil, err
		}
		result = append(result, tokens...)

		f.Close()
	}

	return result, nil
}

type Token struct {
	value    int
	filename string
	line     int
	column   int
}

type Lexer struct {
	tokens []*Token
	pos    int
	e      error
}

type Error struct {
	Message  string
	Filename string
	Line     int
	Column   int
}

func (e *Error) Error() string {
	return e.Message
}

func NewLexer(tokens []*Token) *Lexer {
	return &Lexer{tokens: tokens, pos: -1}
}

func isIdentRune(ch rune, i int) bool {
	return ch == '_' || ch == '.' || ch == '/' || ch == ':' || ch == '-' || ch == '+' || ch == '*' || ch == '?' || ch == '=' || ch == '&' || ch == '@' || unicode.IsLetter(ch) || unicode.IsDigit(ch)
}

func (l *Lexer) curToken() *Token {
	return l.tokens[l.pos]
}

func (l *Lexer) nextToken() *Token {
	l.pos++
	return l.tokens[l.pos]
}

func (l *Lexer) Lex(lval *yySymType) int {
	if (len(l.tokens) - 1) == l.pos {
		return EOF
	}
	token := l.nextToken()
	return token.value
}

func (l *Lexer) Error(msg string) {
	token := l.curToken()
	l.e = &Error{
		Filename: token.filename,
		Line:     token.line,
		Column:   token.column,
		Message:  msg,
	}
}

func Parse(src io.Reader, filename string) error {
	yyErrorVerbose = true
	t := NewTokenizer(src, filename)
	tokens, err := t.NextAll()
	if err != nil {
		return err
	}
	l := NewLexer(tokens)
	if ret := yyParse(l); ret != 0 {
		return l.e
	}
	return l.e
}
<commit_msg>Use new tokenizer for 'include' file instead of new scanner<commit_after>package parser

import (
	"fmt"
	"io"
	"net"
	"net/mail"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/scanner"
	"unicode"

	"github.com/yuuki/gokc/log"
)

const (
	EOF = 0
)

var SYMBOL_TABLES = map[string]int{
	"{": LB,
	"}": RB,

	"global_defs":                     GLOBALDEFS,
	"notification_email":              NOTIFICATION_EMAIL,
	"notification_email_from":         NOTIFICATION_EMAIL_FROM,
	"smtp_server":                     SMTP_SERVER,
	"smtp_connect_timeout":            SMTP_CONNECT_TIMEOUT,
	"router_id":                       ROUTER_ID,
	"lvs_id":                          LVS_ID,
	"vrrp_mcast_group4":               VRRP_MCAST_GROUP4,
	"vrrp_mcast_group6":               VRRP_MCAST_GROUP6,
	"vrrp_garp_master_delay":          VRRP_GARP_MASTER_DELAY,
	"vrrp_garp_master_repeat":         VRRP_GARP_MASTER_REPEAT,
	"vrrp_garp_master_refresh":        VRRP_GARP_MASTER_REFRESH,
	"vrrp_garp_master_refresh_repeat": VRRP_GARP_MASTER_REFRESH_REPEAT,
	"vrrp_version":                    VRRP_VERSION,

	"static_ipaddress": STATIC_IPADDRESS,
	"static_routes":    STATIC_ROUTES,
	"static_rules":     STATIC_RULES,

	"vrrp_sync_group": VRRP_SYNC_GROUP,
	"group":           GROUP,

	"vrrp_instance":              VRRP_INSTANCE,
	"use_vmac":                   USE_VMAC,
	"version":                    VERSION,
	"vmac_xmit_base":             VMAC_XMIT_BASE,
	"native_ipv6":                NATIVE_IPV6,
	"interface":                  INTERFACE,
	"mcast_src_ip":               MCAST_SRC_IP,
	"unicast_src_ip":             UNICAST_SRC_IP,
	"unicast_peer":               UNICAST_PEER,
	"lvs_sync_daemon_interface":  LVS_SYNC_DAEMON_INTERFACE,
	"virtual_router_id":          VIRTUAL_ROUTER_ID,
	"nopreempt":                  NOPREEMPT,
	"priority":                   PRIORITY,
	"advert_int":                 ADVERT_INT,
	"virtual_ipaddress":          VIRTUAL_IPADDRESS,
	"virtual_ipaddress_excluded": VIRTUAL_IPADDRESS_EXCLUDED,
	"virtual_routes":             VIRTUAL_ROUTES,
	"state":                      STATE,
	"MASTER":                     MASTER,
	"BACKUP":                     BACKUP,
	"garp_master_delay":          GARP_MASTER_DELAY,
	"smtp_alert":                 SMTP_ALERT,
	"authentication":             AUTHENTICATION,
	"auth_type":                  AUTH_TYPE,
	"auth_pass":                  AUTH_PASS,
	"PASS":                       PASS,
	"AH":                         AH,
	"label":                      LABEL,
	"dev":                        DEV,
	"scope":                      SCOPE,
	"site":                       SITE,
	"link":                       LINK,
	"host":                       HOST,
	"nowhere":                    NOWHERE,
	"global":                     GLOBAL,
	"brd":                        BRD,
	"src":                        SRC,
	"from":                       FROM,
	"to":                         TO,
	"via":                        VIA,
	"gw":                         GW,
	"or":                         OR,
	"table":                      TABLE,
	"metric":                     METRIC,
	"blackhole":                  BLACKHOLE,
	"track_interface":            TRACK_INTERFACE,
	"track_script":               TRACK_SCRIPT,
	"dont_track_primary":         DONT_TRACK_PRIMARY,
	"notify_master":              NOTIFY_MASTER,
	"notify_backup":              NOTIFY_BACKUP,
	"notify_fault":               NOTIFY_FAULT,
	"notify_stop":                NOTIFY_STOP,
	"notify":                     NOTIFY,

	"vrrp_script": VRRP_SCRIPT,
	"script":      SCRIPT,
	"interval":    INTERVAL,
	"timeout":     TIMEOUT,
	"fall":        FALL,
	"rise":        RISE,

	"virtual_server_group": VIRTUAL_SERVER_GROUP,
	"fwmark":               FWMARK,

	"virtual_server": VIRTUAL_SERVER,
	"delay_loop":     DELAY_LOOP,
	"lb_algo":        LB_ALGO,
	"lb_kind":        LB_KIND,
	"lvs_sched":      LVS_SCHED,
	"lvs_method":     LVS_METHOD,
	"rr":             RR,
	"wrr":            WRR,
	"lc":             LC,
	"wlc":            WLC,
	"fo":             FO,
	"ovf":            OVF,
	"lblc":           LBLC,
	"lblcr":          LBLCR,
	"sh":             SH,
	"dh":             DH,
	"sed":            SED,
	"nq":             NQ,
	"NAT":            NAT,
	"DR":             DR,
	"TUN":            TUN,
	"persistence_timeout": PERSISTENCE_TIMEOUT,
	"protocol":            PROTOCOL,
	"TCP":                 TCP,
	"UDP":                 UDP,
	"sorry_server":        SORRY_SERVER,
	"real_server":         REAL_SERVER,
	"weight":              WEIGHT,
	"inhibit_on_failure":  INHIBIT_ON_FAILURE,
	"TCP_CHECK":           TCP_CHECK,
	"HTTP_GET":            HTTP_GET,
	"SSL_GET":             SSL_GET,
	"SMTP_CHECK":          SMTP_CHECK,
	"DNS_CHECK":           DNS_CHECK,
	"MISC_CHECK":          MISC_CHECK,
	"url":                 URL,
	"path":                PATH,
	"digest":              DIGEST,
	"status_code":         STATUS_CODE,
	"connect_timeout":     CONNECT_TIMEOUT,
	"connect_port":        CONNECT_PORT,
	"connect_ip":          CONNECT_IP,
	"bindto":              BINDTO,
	"bind_port":           BIND_PORT,
	"retry":               RETRY,
	"helo_name":           HELO_NAME,
	"delay_before_retry":  DELAY_BEFORE_RETRY,
	"type":                TYPE,
	"name":                NAME,
	"misc_path":           MISC_PATH,
	"misc_timeout":        MISC_TIMEOUT,
	"warmup":              WARMUP,
	"misc_dynamic":        MISC_DYNAMIC,
	"nb_get_retry":        NB_GET_RETRY,
	"virtualhost":         VIRTUALHOST,
	"alpha":               ALPHA,
	"omega":               OMEGA,
	"quorum":              QUORUM,
	"hysteresis":          HYSTERESIS,
	"quorum_up":           QUORUM_UP,
	"quorum_down":         QUORUM_DOWN,
}

type Tokenizer struct {
	scanner  scanner.Scanner
	filename string
}

func NewTokenizer(src io.Reader, filename string) *Tokenizer {
	var t Tokenizer
	t.scanner.Init(src)
	t.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats | scanner.ScanChars | scanner.ScanRawStrings | scanner.ScanComments | scanner.SkipComments
	t.scanner.IsIdentRune = isIdentRune
	t.filename = filename
	return &t
}

func (t *Tokenizer) NextAll() ([]*Token, error) {
	var result []*Token

	for {
		token, s := t.scanNextToken()

		for s == "include" {
			token, s = t.scanNextToken()

			tokens, err := t.scanInclude(s)
			if err != nil {
				return nil, err
			}
			result = append(result, tokens...)

			token, s = t.scanNextToken()
		}

		if token == scanner.EOF {
			break
		}

		if token == scanner.Ident || token == scanner.String {
			token = STRING
		}

		if _, err := strconv.Atoi(s); err == nil {
			token = NUMBER
		}

		if ip := net.ParseIP(s); ip != nil {
			if ip.To4() != nil {
				token = IPV4
			} else if ip.To16() != nil {
				token = IPV6
			} else {
				log.Infof("warning: %s may be IP address?", s)
			}
		}

		if _, _, err := net.ParseCIDR(s); err == nil {
			token = IP_CIDR
		}

		// IPADDR_RANGE(XXX.YYY.ZZZ.WWW-VVV)
		if ss := strings.Split(s, "-"); len(ss) == 2 {
			if net.ParseIP(ss[0]) != nil {
				if ok, _ := regexp.MatchString(`^[\d]{1,3}$`, ss[1]); ok {
					token = IPADDR_RANGE
				}
			}
		}

		if ok, _ := regexp.MatchString(`^[[:xdigit:]]{32}$`, s); ok {
			token = HEX32
		}

		if ok, _ := regexp.MatchString(`/^([[:alnum:]./-_])*`, s); ok {
			token = PATHSTR
		}

		if _, err := mail.ParseAddress(s); err == nil {
			token = EMAIL
		}

		if _, ok := SYMBOL_TABLES[s]; ok {
			token = SYMBOL_TABLES[s]
		}

		result = append(result, &Token{
			value:    token,
			filename: t.filename,
			line:     t.scanner.Line,
			column:   t.scanner.Column,
		})
	}

	return result, nil
}

func skipComments(scanner *scanner.Scanner) {
	ch := scanner.Next()
	for ch != '\n' && ch >= 0 {
		ch = scanner.Next()
	}
}

func (t *Tokenizer) scanNextToken() (int, string) {
	token := int(t.scanner.Scan())
	s := t.scanner.TokenText()

	for s == "!" || s == "#" {
		skipComments(&t.scanner)

		token = int(t.scanner.Scan())
		s = t.scanner.TokenText()
	}

	log.Debugf("token text: %s\n", s)

	return token, s
}

func (t *Tokenizer) scanInclude(rawfilename string) ([]*Token, error) {
	curDir, err := filepath.Abs(".")
	if err != nil {
		return nil, err
	}

	baseDir := filepath.Dir(t.filename)
	os.Chdir(baseDir)
	defer os.Chdir(curDir)

	rawpaths, err := filepath.Glob(rawfilename)
	if err != nil {
		return nil, err
	}

	if len(rawpaths) < 1 {
		return nil, fmt.Errorf("warning: %s: No such file or directory", rawfilename)
	}

	var result []*Token
	for _, rawpath := range rawpaths {
		log.Verbosef("--> Parsing ... %s\n", rawpath)

		f, err := os.Open(rawpath)
		if err != nil {
			return nil, err
		}

		child := NewTokenizer(f, rawpath)
		tokens, err := child.NextAll()
		if err != nil {
			return nil, err
		}
		result = append(result, tokens...)

		f.Close()
	}

	return result, nil
}

type Token struct {
	value    int
	filename string
	line     int
	column   int
}

type Lexer struct {
	tokens []*Token
	pos    int
	e      error
}

type Error struct {
	Message  string
	Filename string
	Line     int
	Column   int
}

func (e *Error) Error() string {
	return e.Message
}

func NewLexer(tokens []*Token) *Lexer {
	return &Lexer{tokens: tokens, pos: -1}
}

func isIdentRune(ch rune, i int) bool {
	return ch == '_' || ch == '.' || ch == '/' || ch == ':' || ch == '-' || ch == '+' || ch == '*' || ch == '?' || ch == '=' || ch == '&' || ch == '@' || unicode.IsLetter(ch) || unicode.IsDigit(ch)
}

func (l *Lexer) curToken() *Token {
	return l.tokens[l.pos]
}

func (l *Lexer) nextToken() *Token {
	l.pos++
	return l.tokens[l.pos]
}

func (l *Lexer) Lex(lval *yySymType) int {
	if (len(l.tokens) - 1) == l.pos {
		return EOF
	}
	token := l.nextToken()
	return token.value
}

func (l *Lexer) Error(msg string) {
	token := l.curToken()
	l.e = &Error{
		Filename: token.filename,
		Line:     token.line,
		Column:   token.column,
		Message:  msg,
	}
}

func Parse(src io.Reader, filename string) error {
	yyErrorVerbose = true
	t := NewTokenizer(src, filename)
	tokens, err := t.NextAll()
	if err != nil {
		return err
	}
	l := NewLexer(tokens)
	if ret := yyParse(l); ret != 0 {
		return l.e
	}
	return l.e
}
<|endoftext|>


<commit_before>package writeaheadlog

import (
	"io"
	"io/ioutil"
	"os"
	"sync"

	"github.com/NebulousLabs/errors"
	"github.com/NebulousLabs/fastrand"
)

// These interfaces define the wal's dependencies. Using the smallest
// interface possible makes it easier to mock these dependencies in testing.
type (
	dependencies interface {
		disrupt(string) bool
		readFile(string) ([]byte, error)
		openFile(string, int, os.FileMode) (file, error)
		create(string) (file, error)
		remove(string) error
	}

	// file implements all of the methods that can be called on an os.File.
	file interface {
		io.ReadWriteCloser
		Name() string
		ReadAt([]byte, int64) (int, error)
		Sync() error
		WriteAt([]byte, int64) (int, error)
		Stat() (os.FileInfo, error)
	}
)

// dependencyCommitFail corrupts the first page of a transaction when it
// is committed
type dependencyCommitFail struct {
	prodDependencies
}

func (*dependencyCommitFail) disrupt(s string) bool {
	if s == "CommitFail" {
		return true
	}
	return false
}

// dependencyReleaseFail corrupts the first page of a transaction when it
// is released
type dependencyReleaseFail struct {
	prodDependencies
}

func (*dependencyReleaseFail) disrupt(s string) bool {
	if s == "ReleaseFail" {
		return true
	}
	return false
}

// prodDependencies is a passthrough to the standard library calls
type prodDependencies struct{}

func (*prodDependencies) disrupt(string) bool { return false }
func (*prodDependencies) readFile(path string) ([]byte, error) {
	return ioutil.ReadFile(path)
}
func (*prodDependencies) openFile(path string, flag int, perm os.FileMode) (file, error) {
	return os.OpenFile(path, flag, perm)
}
func (*prodDependencies) create(path string) (file, error) {
	return os.Create(path)
}
func (*prodDependencies) remove(path string) error {
	return os.Remove(path)
}

// faultyDiskDependency implements dependencies that simulate a faulty disk.
type faultyDiskDependency struct {
	// failDenominator determines how likely it is that a write will fail,
	// defined as 1/failDenominator. Each write call increments
	// failDenominator, and it starts at 2. This means that the more calls to
	// WriteAt, the less likely the write is to fail. All calls will start
	// automatically failing after writeLimit writes.
	failDenominator uint64
	writeLimit      uint64
	failed          bool
	disabled        bool
	mu              sync.Mutex
}

// newFaultyDiskDependency creates a dependency that can be used to simulate a
// failing disk. writeLimit is the maximum number of writes the disk will
// endure before failing
func newFaultyDiskDependency(writeLimit uint64) faultyDiskDependency {
	return faultyDiskDependency{
		failDenominator: uint64(3),
		writeLimit:      writeLimit,
	}
}

func (*faultyDiskDependency) disrupt(s string) bool {
	return s == "FaultyDisk"
}
func (*faultyDiskDependency) readFile(path string) ([]byte, error) {
	return ioutil.ReadFile(path)
}
func (d *faultyDiskDependency) openFile(path string, flag int, perm os.FileMode) (file, error) {
	f, err := os.OpenFile(path, flag, perm)
	if err != nil {
		return nil, err
	}
	return d.newFaultyFile(f), nil
}
func (d *faultyDiskDependency) create(path string) (file, error) {
	if d.failed {
		return nil, errors.New("failed to create file (faulty disk)")
	}

	f, err := os.Create(path)
	if err != nil {
		return nil, err
	}
	return d.newFaultyFile(f), nil
}
func (d *faultyDiskDependency) remove(path string) error {
	d.mu.Lock()
	defer d.mu.Unlock()

	if d.disabled {
		return os.Remove(path)
	}

	fail := fastrand.Intn(int(d.failDenominator)) == 0
	d.failDenominator++
	if fail || d.failed || d.failDenominator >= d.writeLimit {
		d.failed = true
		return nil
	}

	return os.Remove(path)
}

// faultyFile implements a file that simulates a faulty disk.
type faultyFile struct {
	d    *faultyDiskDependency
	file *os.File
}

func (f *faultyFile) Read(p []byte) (int, error) {
	return f.file.Read(p)
}
func (f *faultyFile) Write(p []byte) (int, error) {
	f.d.mu.Lock()
	defer f.d.mu.Unlock()

	if f.d.disabled {
		return f.file.Write(p)
	}

	fail := fastrand.Intn(int(f.d.failDenominator)) == 0
	f.d.failDenominator++
	if fail || f.d.failed || f.d.failDenominator >= f.d.writeLimit {
		f.d.failed = true
		// Write random amount of bytes on failure
		return f.file.Write(fastrand.Bytes(fastrand.Intn(len(p) + 1)))
	}

	return f.file.Write(p)
}
func (f *faultyFile) Close() error { return f.file.Close() }
func (f *faultyFile) Name() string {
	return f.file.Name()
}
func (f *faultyFile) ReadAt(p []byte, off int64) (int, error) {
	return f.file.ReadAt(p, off)
}
func (f *faultyFile) WriteAt(p []byte, off int64) (int, error) {
	f.d.mu.Lock()
	defer f.d.mu.Unlock()

	if f.d.disabled {
		return f.file.WriteAt(p, off)
	}

	fail := fastrand.Intn(int(f.d.failDenominator)) == 0
	f.d.failDenominator++
	if fail || f.d.failed || f.d.failDenominator >= f.d.writeLimit {
		f.d.failed = true
		// Write random amount of bytes on failure
		return f.file.WriteAt(fastrand.Bytes(fastrand.Intn(len(p)+1)), off)
	}
	return f.file.WriteAt(p, off)
}
func (f *faultyFile) Stat() (os.FileInfo, error) {
	return f.file.Stat()
}
func (f *faultyFile) Sync() error {
	f.d.mu.Lock()
	defer f.d.mu.Unlock()

	if !f.d.disabled && f.d.failed {
		return errors.New("could not write to disk (faultyDisk)")
	}
	return f.file.Sync()
}

// newFaultyFile creates a new faulty file around the provided file handle.
func (d *faultyDiskDependency) newFaultyFile(f *os.File) *faultyFile {
	return &faultyFile{d: d, file: f}
}

// reset resets the failDenominator and the failed flag of the dependency
func (d *faultyDiskDependency) reset() {
	d.mu.Lock()
	d.failDenominator = 3
	d.failed = false
	d.mu.Unlock()
}

// disabled allows the caller to temporarily disable the dependency
func (d *faultyDiskDependency) disable(b bool) {
	d.mu.Lock()
	d.disabled = b
	d.mu.Unlock()
}
<commit_msg>faulty disk writes randomly scramble written data<commit_after>package writeaheadlog

import (
	"io"
	"io/ioutil"
	"os"
	"sync"

	"github.com/NebulousLabs/errors"
	"github.com/NebulousLabs/fastrand"
)

// These interfaces define the wal's dependencies. Using the smallest
// interface possible makes it easier to mock these dependencies in testing.
type (
	dependencies interface {
		disrupt(string) bool
		readFile(string) ([]byte, error)
		openFile(string, int, os.FileMode) (file, error)
		create(string) (file, error)
		remove(string) error
	}

	// file implements all of the methods that can be called on an os.File.
	file interface {
		io.ReadWriteCloser
		Name() string
		ReadAt([]byte, int64) (int, error)
		Sync() error
		WriteAt([]byte, int64) (int, error)
		Stat() (os.FileInfo, error)
	}
)

// dependencyCommitFail corrupts the first page of a transaction when it
// is committed
type dependencyCommitFail struct {
	prodDependencies
}

func (*dependencyCommitFail) disrupt(s string) bool {
	if s == "CommitFail" {
		return true
	}
	return false
}

// dependencyReleaseFail corrupts the first page of a transaction when it
// is released
type dependencyReleaseFail struct {
	prodDependencies
}

func (*dependencyReleaseFail) disrupt(s string) bool {
	if s == "ReleaseFail" {
		return true
	}
	return false
}

// prodDependencies is a passthrough to the standard library calls
type prodDependencies struct{}

func (*prodDependencies) disrupt(string) bool { return false }
func (*prodDependencies) readFile(path string) ([]byte, error) {
	return ioutil.ReadFile(path)
}
func (*prodDependencies) openFile(path string, flag int, perm os.FileMode) (file, error) {
	return os.OpenFile(path, flag, perm)
}
func (*prodDependencies) create(path string) (file, error) {
	return os.Create(path)
}
func (*prodDependencies) remove(path string) error {
	return os.Remove(path)
}

// faultyDiskDependency implements dependencies that simulate a faulty disk.
type faultyDiskDependency struct {
	// failDenominator determines how likely it is that a write will fail,
	// defined as 1/failDenominator. Each write call increments
	// failDenominator, and it starts at 2. This means that the more calls to
	// WriteAt, the less likely the write is to fail. All calls will start
	// automatically failing after writeLimit writes.
	failDenominator uint64
	writeLimit      uint64
	failed          bool
	disabled        bool
	mu              sync.Mutex
}

// newFaultyDiskDependency creates a dependency that can be used to simulate a
// failing disk. writeLimit is the maximum number of writes the disk will
// endure before failing
func newFaultyDiskDependency(writeLimit uint64) faultyDiskDependency {
	return faultyDiskDependency{
		failDenominator: uint64(3),
		writeLimit:      writeLimit,
	}
}

func (*faultyDiskDependency) disrupt(s string) bool {
	return s == "FaultyDisk"
}
func (*faultyDiskDependency) readFile(path string) ([]byte, error) {
	return ioutil.ReadFile(path)
}
func (d *faultyDiskDependency) openFile(path string, flag int, perm os.FileMode) (file, error) {
	f, err := os.OpenFile(path, flag, perm)
	if err != nil {
		return nil, err
	}
	return d.newFaultyFile(f), nil
}
func (d *faultyDiskDependency) create(path string) (file, error) {
	if d.failed {
		return nil, errors.New("failed to create file (faulty disk)")
	}

	f, err := os.Create(path)
	if err != nil {
		return nil, err
	}
	return d.newFaultyFile(f), nil
}
func (d *faultyDiskDependency) remove(path string) error {
	d.mu.Lock()
	defer d.mu.Unlock()

	if d.disabled {
		return os.Remove(path)
	}

	fail := fastrand.Intn(int(d.failDenominator)) == 0
	d.failDenominator++
	if fail || d.failed || d.failDenominator >= d.writeLimit {
		d.failed = true
		return nil
	}

	return os.Remove(path)
}

// scrambleData takes some data as input and replaces parts of it randomly with
// random data
func scrambleData(d []byte) []byte {
	randomData := fastrand.Bytes(len(d))
	scrambled := make([]byte, len(d), len(d))
	for i := 0; i < len(d); i++ {
		if fastrand.Intn(4) == 0 { // 25% chance to replace byte
			scrambled[i] = randomData[i]
		} else {
			scrambled[i] = d[i]
		}
	}
	return scrambled
}

// faultyFile implements a file that simulates a faulty disk.
type faultyFile struct {
	d    *faultyDiskDependency
	file *os.File
}

func (f *faultyFile) Read(p []byte) (int, error) {
	return f.file.Read(p)
}
func (f *faultyFile) Write(p []byte) (int, error) {
	f.d.mu.Lock()
	defer f.d.mu.Unlock()

	if f.d.disabled {
		return f.file.Write(p)
	}

	fail := fastrand.Intn(int(f.d.failDenominator)) == 0
	f.d.failDenominator++
	if fail || f.d.failed || f.d.failDenominator >= f.d.writeLimit {
		f.d.failed = true
		// scramble data
		return f.file.Write(scrambleData(p))
	}
	return f.file.Write(p)
}
func (f *faultyFile) Close() error { return f.file.Close() }
func (f *faultyFile) Name() string {
	return f.file.Name()
}
func (f *faultyFile) ReadAt(p []byte, off int64) (int, error) {
	return f.file.ReadAt(p, off)
}
func (f *faultyFile) WriteAt(p []byte, off int64) (int, error) {
	f.d.mu.Lock()
	defer f.d.mu.Unlock()

	if f.d.disabled {
		return f.file.WriteAt(p, off)
	}

	fail := fastrand.Intn(int(f.d.failDenominator)) == 0
	f.d.failDenominator++
	if fail || f.d.failed || f.d.failDenominator >= f.d.writeLimit {
		f.d.failed = true
		// scramble data
		return f.file.WriteAt(scrambleData(p), off)
	}
	return f.file.WriteAt(p, off)
}
func (f *faultyFile) Stat() (os.FileInfo, error) {
	return f.file.Stat()
}
func (f *faultyFile) Sync() error {
	f.d.mu.Lock()
	defer f.d.mu.Unlock()

	if !f.d.disabled && f.d.failed {
		return errors.New("could not write to disk (faultyDisk)")
	}
	return f.file.Sync()
}

// newFaultyFile creates a new faulty file around the provided file handle.
func (d *faultyDiskDependency) newFaultyFile(f *os.File) *faultyFile {
	return &faultyFile{d: d, file: f}
}

// reset resets the failDenominator and the failed flag of the dependency
func (d *faultyDiskDependency) reset() {
	d.mu.Lock()
	d.failDenominator = 3
	d.failed = false
	d.mu.Unlock()
}

// disabled allows the caller to temporarily disable the dependency
func (d *faultyDiskDependency) disable(b bool) {
	d.mu.Lock()
	d.disabled = b
	d.mu.Unlock()
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"regexp"

	"gopkg.in/mgo.v2/bson"
)

// Email helpers

type Email string

var (
	ErrEmptyEmail       = errors.New("empty email")
	ErrWrongEmailFormat = errors.New("wrong email format")
)

//Long and strange regexp to validate email format.
var emailRegex = regexp.MustCompile(`^(([^<>()\[\]\\.,;:\s@“]+(\.[^<>()\[\]\\.,;:\s@“]+)*)|(“.+“))@((\[[0-9]{1,3}\.
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`)

// Validates email
func (e Email) Validate() error {
	if len(e) == 0 {
		return ErrEmptyEmail
	}
	if !emailRegex.MatchString(string(e)) {
		return ErrWrongEmailFormat
	}
	return nil
}

// Password helpers

type Password string

var (
	ErrEmptyPassword       = errors.New("empty password")
	ErrWrongPasswordFormat = errors.New("wrong password format")
)

var passwordRegex = regexp.MustCompile(`^[0-9a-zA-Z\s\r\n@!#$^%&*()+=\-\[\]\\';,./{}|":<>?]{3,14}$`)

// Validates passowrd
func (p Password) Validate() error {
	if len(p) == 0 {
		return ErrEmptyPassword
	}
	if !passwordRegex.MatchString(string(p)) {
		return ErrWrongPasswordFormat
	}
	return nil
}

// Name

type Name string

var nameRegex = regexp.MustCompile(`^[a-zA-Z].{1,49}$`)

var (
	ErrEmptyName       = errors.New("empty name")
	ErrWrongNameFormat = errors.New("wrong name format")
)

// Validates names
func (n Name) Validate() error {
	if len(n) == 0 {
		return ErrEmptyName
	}
	if !nameRegex.MatchString(string(n)) {
		return ErrWrongNameFormat
	}
	return nil
}

// Text

type Text string

var (
	ErrTextTooLong = errors.New("text too long")

	MaxTextLen = 500
)

// Validate text
func (t Text) Validate() error {
	if len(t) > MaxTextLen {
		return ErrTextTooLong
	}
	return nil
}

// AutoId helpers

type AutoId bson.ObjectId

var ErrIdMustBeOmitted = errors.New("id must be omitted")

// Validates generated id
func (id AutoId) Validate() error {
	if id != AutoId("") {
		return ErrIdMustBeOmitted
	}
	return nil
}

// RequiredId helpers

type RequiredId bson.ObjectId

var ErrIdMustBePresent = errors.New("id must be present")

// Validates required id
func (id RequiredId) Validate() error {
	if id == RequiredId("") {
		return ErrIdMustBePresent
	}
	return nil
}
<commit_msg>Add optional ids.<commit_after>package models

import (
	"errors"
	"regexp"

	"gopkg.in/mgo.v2/bson"
)

// Email helpers

type Email string

var (
	ErrEmptyEmail       = errors.New("empty email")
	ErrWrongEmailFormat = errors.New("wrong email format")
)

//Long and strange regexp to validate email format.
var emailRegex = regexp.MustCompile(`^(([^<>()\[\]\\.,;:\s@“]+(\.[^<>()\[\]\\.,;:\s@“]+)*)|(“.+“))@((\[[0-9]{1,3}\.
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`)

// Validates email
func (e Email) Validate() error {
	if len(e) == 0 {
		return ErrEmptyEmail
	}
	if !emailRegex.MatchString(string(e)) {
		return ErrWrongEmailFormat
	}
	return nil
}

// Password helpers

type Password string

var (
	ErrEmptyPassword       = errors.New("empty password")
	ErrWrongPasswordFormat = errors.New("wrong password format")
)

var passwordRegex = regexp.MustCompile(`^[0-9a-zA-Z\s\r\n@!#$^%&*()+=\-\[\]\\';,./{}|":<>?]{3,14}$`)

// Validates passowrd
func (p Password) Validate() error {
	if len(p) == 0 {
		return ErrEmptyPassword
	}
	if !passwordRegex.MatchString(string(p)) {
		return ErrWrongPasswordFormat
	}
	return nil
}

// Name

type Name string

var nameRegex = regexp.MustCompile(`^[a-zA-Z].{1,49}$`)

var (
	ErrEmptyName       = errors.New("empty name")
	ErrWrongNameFormat = errors.New("wrong name format")
)

// Validates names
func (n Name) Validate() error {
	if len(n) == 0 {
		return ErrEmptyName
	}
	if !nameRegex.MatchString(string(n)) {
		return ErrWrongNameFormat
	}
	return nil
}

// Text

type Text string

var (
	ErrTextTooLong = errors.New("text too long")

	MaxTextLen = 500
)

// Validate text
func (t Text) Validate() error {
	if len(t) > MaxTextLen {
		return ErrTextTooLong
	}
	return nil
}

// General Id helpers

type Id bson.ObjectId

var ErrInvalidId = errors.New("invalid id")

// Validates id
func ValidateId(id Id) error {
	// NOTE: By  default id.Valid() checks only id len
	// BTW we could pass id like: bson.ObjectId("12_bytes_len")
	if !bson.IsObjectIdHex(string(id)) {
		return ErrInvalidId
	}
	return nil
}

// AutoId helpers

type AutoId Id

var ErrIdMustBeOmitted = errors.New("id must be omitted")

// Validates generated id
func (id AutoId) Validate() error {
	if id != AutoId("") {
		return ErrIdMustBeOmitted
	}
	return nil
}

// RequiredId helpers

type RequiredId Id

var ErrIdMustBePresent = errors.New("id must be present")

// Validates required id
func (id RequiredId) Validate() error {
	if id == RequiredId("") {
		return ErrIdMustBePresent
	}

	if err := ValidateId(Id(id)); err != nil {
		return err
	}
	return nil
}

// Optional Id helpers

type OptionalId bson.ObjectId

// Validates optional id
func (id OptionalId) Validate() error {
	if err := AutoId(id).Validate(); err == nil {
		return nil
	}

	if err := RequiredId(id).Validate(); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Package passwordhash implements safe password hashing and comparison.
//
// Passwords are derived using PBKDF2-SHA256 function with 5000 iterations (by default),
// with 32-byte salt and 64-byte output.
package passwordhash

import (
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"fmt"
	"github.com/dchest/pbkdf2"
)

// PasswordHash represents storage for password hash and salt.
type PasswordHash struct {
	Iter int
	Salt []byte
	Hash []byte
}

const (
	// Default number of iterations for PBKDF2
	DefaultIterations = 5000
	// Default salt length
	SaltLen = 32
)

// getSalt returns a new random salt.
func getSalt() []byte {
	salt := make([]byte, SaltLen)
	if _, err := rand.Reader.Read(salt); err != nil {
		panic("can't read from random source: " + err.String())
	}
	return salt
}

// New returns a new password hash derived from the provided password,
// a random salt, and the default number of iterations.
func New(password string) *PasswordHash {
	return NewSaltIter(password, getSalt(), DefaultIterations)
}

// NewIter returns a new password hash derived from the provided password,
// the number of iterations, and a random salt.
func NewIter(password string, iter int) *PasswordHash {
	return NewSaltIter(password, getSalt(), iter)
}

// NewSaltIter creates a new password hash from the provided password, salt,
// and the number of iterations.
func NewSaltIter(password string, salt []byte, iter int) *PasswordHash {
	return &PasswordHash{iter, salt,
		pbkdf2.WithHMAC(sha256.New, []byte(password), salt, iter, 64)}
}

// EqualToPassword returns true if the password hash was derived from the provided password.
// This function uses constant time comparison.
func (ph *PasswordHash) EqualToPassword(password string) bool {
	provided := NewSaltIter(password, ph.Salt, ph.Iter)
	return subtle.ConstantTimeCompare(ph.Hash, provided.Hash) == 1
}

// String returns a string representation of the password hash.
func (ph *PasswordHash) String() string {
	return fmt.Sprintf("&PasswordHash{Iterations: %d, Salt: %x, Hash: %x}",
		ph.Iter, ph.Salt, ph.Hash)
}
<commit_msg>Update doc<commit_after>// Package passwordhash implements safe password hashing and comparison.
//
// Passwords are derived using PBKDF2-HMAC-SHA256 function with 5000 iterations
// (by default), 32-byte salt and 64-byte output.
package passwordhash

import (
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"fmt"
	"github.com/dchest/pbkdf2"
)

// PasswordHash represents storage for password hash and salt.
type PasswordHash struct {
	Iter int
	Salt []byte
	Hash []byte
}

const (
	// Default number of iterations for PBKDF2
	DefaultIterations = 5000
	// Default salt length
	SaltLen = 32
)

// getSalt returns a new random salt.
func getSalt() []byte {
	salt := make([]byte, SaltLen)
	if _, err := rand.Reader.Read(salt); err != nil {
		panic("can't read from random source: " + err.String())
	}
	return salt
}

// New returns a new password hash derived from the provided password,
// a random salt, and the default number of iterations.
func New(password string) *PasswordHash {
	return NewSaltIter(password, getSalt(), DefaultIterations)
}

// NewIter returns a new password hash derived from the provided password,
// the number of iterations, and a random salt.
func NewIter(password string, iter int) *PasswordHash {
	return NewSaltIter(password, getSalt(), iter)
}

// NewSaltIter creates a new password hash from the provided password, salt,
// and the number of iterations.
func NewSaltIter(password string, salt []byte, iter int) *PasswordHash {
	return &PasswordHash{iter, salt,
		pbkdf2.WithHMAC(sha256.New, []byte(password), salt, iter, 64)}
}

// EqualToPassword returns true if the password hash was derived from the provided password.
// This function uses constant time comparison.
func (ph *PasswordHash) EqualToPassword(password string) bool {
	provided := NewSaltIter(password, ph.Salt, ph.Iter)
	return subtle.ConstantTimeCompare(ph.Hash, provided.Hash) == 1
}

// String returns a string representation of the password hash.
func (ph *PasswordHash) String() string {
	return fmt.Sprintf("&PasswordHash{Iterations: %d, Salt: %x, Hash: %x}",
		ph.Iter, ph.Salt, ph.Hash)
}
<|endoftext|>