<commit_before>package wuxia

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/gocraft/health"
	"github.com/robertkrimen/otto"
	"github.com/spf13/afero"
)

const (
	scriptsDir = "scripts"
	initDir    = "init"
	planDir    = "plan"
	configFile = "config.json"
	indexFile  = "index.js"
)

//BuildStage the step in the generation process life cycle.
type BuildStage int

// available stages offered by the generator
const (
	StageInit BuildStage = iota
	StageConfig
	StagePlan
	StageExec
)

func (s BuildStage) String() string {
	var rst string
	switch s {
	case StageInit:
		rst = "init"
	case StageConfig:
		rst = "config"
	case StagePlan:
		rst = "plan"
	case StageExec:
		rst = "exec"
	default:
		rst = "unkown stage"
	}
	return rst
}

//buildError error returned when building the static website. The error string
//returned is a json string that encodes the build stage and the message.
type buildError struct {
	Stage   string `json:"stage"`
	Message string `json:"msg"`
}

func buildErr(stage BuildStage, msg string) error {
	return &buildError{Stage: stage.String(), Message: msg}
}

func (b *buildError) Error() string {
	o, err := json.Marshal(b)
	if err != nil {
		return err.Error()
	}
	return string(o)
}

//Generator provides the static website generation capabilities.This is heavily
//integrated with the otto javascript runtime.
type Generator struct {
	vm  *otto.Otto
	sys *System
	fs  afero.Fs

	// This is the absolute path to the root of the project from which the
	// Generator will be operating.
	workDir string
	job     *health.Job
	verbose bool
}

//NewGenerator retrunes a new  Generator.
func NewGenerator(vm *otto.Otto, sys *System, fs afero.Fs) *Generator {
	return &Generator{
		vm:  vm,
		sys: sys,
		fs:  fs,
	}
}

//Init initializes the build process. Any stages after this will have the generator
//already bootstraped.
//
// It is possible to bootstrap the generator from the project( User's side) by
// providing an entry javascript file in the default path of
// scripts/init/index.js which will be executed and you can overide the default
// entry script which is evaluated internally
//
// Initialzation is offloaded to the javascript runtine of the generator..Any
// error returned is a build error.
func (g *Generator) Init() error {
	if g.verbose {
		g.job.Event("initializing_generator")
	}
	_ = g.vm.Set("sys", func(call otto.FunctionCall) otto.Value {
		data, err := json.Marshal(g.sys)
		if err != nil {
			panicOtto(err)
		}
		val, err := call.Otto.Call("JSON.parse", nil, string(data))
		if err != nil {
			panicOtto(err)
		}
		return val
	})
	_, err := g.vm.Eval(entryScript())
	if err != nil {
		return buildErr(StageInit, err.Error())
	}

	_ = g.vm.Set("fileTree", fileTree(g.fs, g.workDir))

	// evaluate project provided entry script if provided. We ignore if the file
	// is not provided but any errors arsing from evaluating a provided script is
	// a built error.
	entryFile := filepath.Join(scriptsDir, initDir, indexFile)
	err = g.evaluateFile(entryFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return buildErr(StageInit, err.Error())
		}
	}
	if g.verbose {
		g.job.EventKv("initializing_generator.complete",
			health.Kvs{
				"project": g.sys.Config.ProjectName,
			})
	}
	return nil
}

//fileTree provides an array of all files found in the root( which is suppose to
//be the working directory.
//
//The file index is built only once and evaluated once too, then it is cached.
//This operates in the otto runtime.
func fileTree(fs afero.Fs, root string) func(otto.FunctionCall) otto.Value {
	var tree []string
	var v otto.Value
	return func(call otto.FunctionCall) otto.Value {
		if tree != nil {
			return v
		}
		ferr := afero.Walk(fs, "/", func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.IsDir() {
				tree = append(tree, path)
			}
			return nil
		})
		if ferr != nil {
			tree = nil
			panicOtto(ferr.Error())
		}
		v, _ = call.Otto.ToValue(tree)
		return v
	}
}

func defaultSystem() *System {
	return &System{
		Boot: &Boot{
			ConfigiFile: configFile,
			PlanFile:    "index.js",
		},
	}
}

func defaultPlan() *Plan {
	return &Plan{}
}

// opens the file in the specified path and evaluates it withing the context of
// the javascript runtine.
//
// The evaluated javascript code can mutate the global state. Use execFile to
// execute the javascript without mutating the state of the generato'r
// javascript runtime.
//
// TODO: (gernest) implement callFile if necessary
func (g *Generator) evaluateFile(path string) error {
	f, err := g.fs.Open(path)
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()
	data, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}
	_, err = g.vm.Eval(data)
	if err != nil {
		return err
	}
	return nil
}

func defaultVM(sys *System) *otto.Otto {
	return otto.New()
}

//Config configures the generator.
func (g *Generator) Config() error {
	if g.verbose {
		g.job.Event("configuring_generator")
	}
	if g.sys == nil {
		g.sys = defaultSystem()
	}
	if g.vm == nil {
		g.vm = defaultVM(g.sys)
	}
	// Properly set working directory/
	if g.workDir == "" {
		wd, err := os.Getwd()
		if err != nil {
			return buildErr(StageInit, err.Error())
		}
		g.workDir = wd
	}

	// ensure everything is relative to the working directory
	g.fs = afero.NewBasePathFs(g.fs, g.workDir)
	af := afero.Afero{Fs: g.fs}
	data, err := af.ReadFile(configFile)
	if err != nil {
		return buildErr(StageInit, err.Error())
	}
	cfg := &Config{}
	err = json.Unmarshal(data, cfg)
	if err != nil {
		return buildErr(StageInit, err.Error())
	}
	g.sys.Config = cfg

	// Add reuire
	req := newRequire(g.fs, scriptsDir)
	err = g.registerBuiltin(req)
	if err != nil {
		return buildErr(StageInit, err.Error())
	}
	_ = g.vm.Set("require", req.load)
	if g.verbose {
		g.job.EventKv("configuring__generator.complete",
			health.Kvs{
				"project": g.sys.Config.ProjectName,
			})
	}
	return nil
}

func (g *Generator) Plan() error {
	pFile := filepath.Join(scriptsDir, planDir, indexFile)
	err := g.evaluateFile(pFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return buildErr(StagePlan, err.Error())
		}
	}
	v, err := g.vm.Call("getCurrentSys", nil)
	if err != nil {
		return buildErr(StagePlan, err.Error())
	}
	str, err := v.ToString()
	if err != nil {
		return buildErr(StagePlan, err.Error())
	}
	sys := &System{}
	err = json.Unmarshal([]byte(str), sys)
	if err != nil {
		return buildErr(StagePlan, err.Error())
	}
	g.sys = sys
	if g.sys.Plan == nil {
		g.sys.Plan = defaultPlan()
	}
	return nil
}

//Exec executes the plans that are outilend by the Plan method. The Plan method
//should be called before this.
func (g *Generator) Exec() error {
	o, err := g.vm.Call("fileTree", nil)
	if err != nil {
		return buildErr(StageExec, err.Error())
	}
	ov, err := o.Export()
	if err != nil {
		return buildErr(StageExec, err.Error())
	}
	if ov == nil {
		// No files to mess with
	}
	files, ok := ov.([]string)
	if !ok {
		// Some fish
	}
	var list FileList
	p := g.sys.Plan
	for i := range files {
		s, err := p.FindStrategy(files[i])
		if err != nil {
			continue
		}
		f, err := g.execStrategy(files[i], s)
		if err != nil {
			return buildErr(StageExec, err.Error())
		}
		list = append(list, f)
	}
	return g.execPlan(list, p)
}

func (g *Generator) execStrategy(filePath string, s *Strategy) (*File, error) {
	return nil, nil
}

func (g *Generator) execPlan(a FileList, p *Plan) error {
	return nil
}

func (g *Generator) down() error {
	return nil
}

func (g *Generator) registerBuiltin(r *require) error {
	f := &fileSys{}
	f.Fs = g.fs
	v := f.export().ToValue(g.vm)
	r.addToCache("fs", v)
	r.addToCache("markdown", markdown().ToValue(g.vm))
	return nil
}
<commit_msg>Update Generator<commit_after>package wuxia

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/gocraft/health"
	"github.com/robertkrimen/otto"
	"github.com/spf13/afero"
)

const (
	scriptsDir = "scripts"
	initDir    = "init"
	planDir    = "plan"
	configFile = "config.json"
	indexFile  = "index.js"
)

//BuildStage the step in the generation process life cycle.
type BuildStage int

// available stages offered by the generator
const (
	StageInit BuildStage = iota
	StageConfig
	StagePlan
	StageExec
)

func (s BuildStage) String() string {
	var rst string
	switch s {
	case StageInit:
		rst = "init"
	case StageConfig:
		rst = "config"
	case StagePlan:
		rst = "plan"
	case StageExec:
		rst = "exec"
	default:
		rst = "unkown stage"
	}
	return rst
}

//buildError error returned when building the static website. The error string
//returned is a json string that encodes the build stage and the message.
type buildError struct {
	Stage   string `json:"stage"`
	Message string `json:"msg"`
}

func buildErr(stage BuildStage, msg string) error {
	return &buildError{Stage: stage.String(), Message: msg}
}

func (b *buildError) Error() string {
	o, err := json.Marshal(b)
	if err != nil {
		return err.Error()
	}
	return string(o)
}

//Generator provides the static website generation capabilities.This is heavily
//integrated with the otto javascript runtime.
type Generator struct {
	vm  *otto.Otto
	sys *System
	fs  afero.Fs

	// This is the absolute path to the root of the project from which the
	// Generator will be operating.
	workDir string
	job     *health.Job

	Verbose bool

	// when the verbose option is set to true. Here is where the ;og information
	// will be written to.
	Out io.Writer
}

//NewGenerator retrunes a new  Generator.
func NewGenerator(vm *otto.Otto, sys *System, fs afero.Fs) *Generator {
	return &Generator{
		vm:  vm,
		sys: sys,
		fs:  fs,
	}
}

//Init initializes the build process. Any stages after this will have the generator
//already bootstraped.
//
// It is possible to bootstrap the generator from the project( User's side) by
// providing an entry javascript file in the default path of
// scripts/init/index.js which will be executed and you can overide the default
// entry script which is evaluated internally
//
// Initialzation is offloaded to the javascript runtine of the generator..Any
// error returned is a build error.
func (g *Generator) Init() error {
	if g.Verbose {
		g.job.Event("initializing_generator")
	}
	_ = g.vm.Set("sys", func(call otto.FunctionCall) otto.Value {
		data, err := json.Marshal(g.sys)
		if err != nil {
			panicOtto(err)
		}
		val, err := call.Otto.Call("JSON.parse", nil, string(data))
		if err != nil {
			panicOtto(err)
		}
		return val
	})
	_, err := g.vm.Eval(entryScript())
	if err != nil {
		return buildErr(StageInit, err.Error())
	}

	_ = g.vm.Set("fileTree", fileTree(g.fs, g.workDir))

	// evaluate project provided entry script if provided. We ignore if the file
	// is not provided but any errors arsing from evaluating a provided script is
	// a built error.
	entryFile := filepath.Join(scriptsDir, initDir, indexFile)
	err = g.evaluateFile(entryFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return buildErr(StageInit, err.Error())
		}
	}
	if g.Verbose {
		g.job.EventKv("initializing_generator.complete",
			health.Kvs{
				"project": g.sys.Config.ProjectName,
			})
	}
	return nil
}

//fileTree provides an array of all files found in the root( which is suppose to
//be the working directory.
//
//The file index is built only once and evaluated once too, then it is cached.
//This operates in the otto runtime.
func fileTree(fs afero.Fs, root string) func(otto.FunctionCall) otto.Value {
	var tree []string
	var v otto.Value
	return func(call otto.FunctionCall) otto.Value {
		if tree != nil {
			return v
		}
		ferr := afero.Walk(fs, "/", func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.IsDir() {
				tree = append(tree, path)
			}
			return nil
		})
		if ferr != nil {
			tree = nil
			panicOtto(ferr.Error())
		}
		v, _ = call.Otto.ToValue(tree)
		return v
	}
}

func defaultSystem() *System {
	return &System{
		Boot: &Boot{
			ConfigiFile: configFile,
			PlanFile:    "index.js",
		},
	}
}

func defaultPlan() *Plan {
	return &Plan{}
}

// opens the file in the specified path and evaluates it withing the context of
// the javascript runtine.
//
// The evaluated javascript code can mutate the global state. Use execFile to
// execute the javascript without mutating the state of the generato'r
// javascript runtime.
//
// TODO: (gernest) implement callFile if necessary
func (g *Generator) evaluateFile(path string) error {
	f, err := g.fs.Open(path)
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()
	data, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}
	_, err = g.vm.Eval(data)
	if err != nil {
		return err
	}
	return nil
}

func defaultVM(sys *System) *otto.Otto {
	return otto.New()
}

//Config configures the generator.
func (g *Generator) Config() error {
	if g.Verbose {
		g.job.Event("configuring_generator")
	}
	if g.sys == nil {
		g.sys = defaultSystem()
	}
	if g.vm == nil {
		g.vm = defaultVM(g.sys)
	}
	// Properly set working directory/
	if g.workDir == "" {
		wd, err := os.Getwd()
		if err != nil {
			return buildErr(StageInit, err.Error())
		}
		g.workDir = wd
	}

	// ensure everything is relative to the working directory
	g.fs = afero.NewBasePathFs(g.fs, g.workDir)
	af := afero.Afero{Fs: g.fs}
	data, err := af.ReadFile(configFile)
	if err != nil {
		return buildErr(StageInit, err.Error())
	}
	cfg := &Config{}
	err = json.Unmarshal(data, cfg)
	if err != nil {
		return buildErr(StageInit, err.Error())
	}
	g.sys.Config = cfg

	// Add reuire
	req := newRequire(g.fs, scriptsDir)
	err = g.registerBuiltin(req)
	if err != nil {
		return buildErr(StageInit, err.Error())
	}
	_ = g.vm.Set("require", req.load)
	if g.Verbose {
		g.job.EventKv("configuring__generator.complete",
			health.Kvs{
				"project": g.sys.Config.ProjectName,
			})
	}
	return nil
}

func (g *Generator) Plan() error {
	pFile := filepath.Join(scriptsDir, planDir, indexFile)
	err := g.evaluateFile(pFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return buildErr(StagePlan, err.Error())
		}
	}
	v, err := g.vm.Call("getCurrentSys", nil)
	if err != nil {
		return buildErr(StagePlan, err.Error())
	}
	str, err := v.ToString()
	if err != nil {
		return buildErr(StagePlan, err.Error())
	}
	sys := &System{}
	err = json.Unmarshal([]byte(str), sys)
	if err != nil {
		return buildErr(StagePlan, err.Error())
	}
	g.sys = sys
	if g.sys.Plan == nil {
		g.sys.Plan = defaultPlan()
	}
	return nil
}

//Exec executes the plans that are outilend by the Plan method. The Plan method
//should be called before this.
func (g *Generator) Exec() error {
	o, err := g.vm.Call("fileTree", nil)
	if err != nil {
		return buildErr(StageExec, err.Error())
	}
	ov, err := o.Export()
	if err != nil {
		return buildErr(StageExec, err.Error())
	}
	if ov == nil {
		// No files to mess with
	}
	files, ok := ov.([]string)
	if !ok {
		// Some fish
	}
	var list FileList
	p := g.sys.Plan
	for i := range files {
		s, err := p.FindStrategy(files[i])
		if err != nil {
			continue
		}
		f, err := g.execStrategy(files[i], s)
		if err != nil {
			return buildErr(StageExec, err.Error())
		}
		list = append(list, f)
	}
	return g.execPlan(list, p)
}

func (g *Generator) execStrategy(filePath string, s *Strategy) (*File, error) {
	return nil, nil
}

func (g *Generator) execPlan(a FileList, p *Plan) error {
	return nil
}

func (g *Generator) down() error {
	return nil
}

func (g *Generator) registerBuiltin(r *require) error {
	f := &fileSys{}
	f.Fs = g.fs
	v := f.export().ToValue(g.vm)
	r.addToCache("fs", v)
	r.addToCache("markdown", markdown().ToValue(g.vm))
	return nil
}
<|endoftext|>


<commit_before>package boom

import (
	"bytes"
	"encoding/binary"
	"hash"
	"hash/fnv"
	"io"
	"math"
)

// BloomFilter implements a classic Bloom filter. A Bloom filter has a non-zero
// probability of false positives and a zero probability of false negatives.
type BloomFilter struct {
	buckets *Buckets    // filter data
	hash    hash.Hash64 // hash function (kernel for all k functions)
	m       uint        // filter size
	k       uint        // number of hash functions
	count   uint        // number of items added
}

// NewBloomFilter creates a new Bloom filter optimized to store n items with a
// specified target false-positive rate.
func NewBloomFilter(n uint, fpRate float64) *BloomFilter {
	m := OptimalM(n, fpRate)
	return &BloomFilter{
		buckets: NewBuckets(m, 1),
		hash:    fnv.New64(),
		m:       m,
		k:       OptimalK(fpRate),
	}
}

// Capacity returns the Bloom filter capacity, m.
func (b *BloomFilter) Capacity() uint {
	return b.m
}

// K returns the number of hash functions.
func (b *BloomFilter) K() uint {
	return b.k
}

// Count returns the number of items added to the filter.
func (b *BloomFilter) Count() uint {
	return b.count
}

// EstimatedFillRatio returns the current estimated ratio of set bits.
func (b *BloomFilter) EstimatedFillRatio() float64 {
	return 1 - math.Exp((-float64(b.count)*float64(b.k))/float64(b.m))
}

// FillRatio returns the ratio of set bits.
func (b *BloomFilter) FillRatio() float64 {
	sum := uint32(0)
	for i := uint(0); i < b.buckets.Count(); i++ {
		sum += b.buckets.Get(i)
	}
	return float64(sum) / float64(b.m)
}

// Test will test for membership of the data and returns true if it is a
// member, false if not. This is a probabilistic test, meaning there is a
// non-zero probability of false positives but a zero probability of false
// negatives.
func (b *BloomFilter) Test(data []byte) bool {
	lower, upper := hashKernel(data, b.hash)

	// If any of the K bits are not set, then it's not a member.
	for i := uint(0); i < b.k; i++ {
		if b.buckets.Get((uint(lower)+uint(upper)*i)%b.m) == 0 {
			return false
		}
	}

	return true
}

// Add will add the data to the Bloom filter. It returns the filter to allow
// for chaining.
func (b *BloomFilter) Add(data []byte) Filter {
	lower, upper := hashKernel(data, b.hash)

	// Set the K bits.
	for i := uint(0); i < b.k; i++ {
		b.buckets.Set((uint(lower)+uint(upper)*i)%b.m, 1)
	}

	b.count++
	return b
}

// TestAndAdd is equivalent to calling Test followed by Add. It returns true if
// the data is a member, false if not.
func (b *BloomFilter) TestAndAdd(data []byte) bool {
	lower, upper := hashKernel(data, b.hash)
	member := true

	// If any of the K bits are not set, then it's not a member.
	for i := uint(0); i < b.k; i++ {
		idx := (uint(lower) + uint(upper)*i) % b.m
		if b.buckets.Get(idx) == 0 {
			member = false
		}
		b.buckets.Set(idx, 1)
	}

	b.count++
	return member
}

// Reset restores the Bloom filter to its original state. It returns the filter
// to allow for chaining.
func (b *BloomFilter) Reset() *BloomFilter {
	b.buckets.Reset()
	return b
}

// SetHash sets the hashing function used in the filter.
// For the effect on false positive rates see: https://github.com/tylertreat/BoomFilters/pull/1
func (b *BloomFilter) SetHash(h hash.Hash64) {
	b.hash = h
}

// WriteTo writes a binary representation of the BloomFilter to an i/o stream.
// It returns the number of bytes written.
func (b *BloomFilter) WriteTo(stream io.Writer) (int64, error) {
	err := binary.Write(stream, binary.BigEndian, uint64(b.count))
	if err != nil {
		return 0, err
	}
	err = binary.Write(stream, binary.BigEndian, uint64(b.m))
	if err != nil {
		return 0, err
	}
	err = binary.Write(stream, binary.BigEndian, uint64(b.k))
	if err != nil {
		return 0, err
	}
	if _, err := b.buckets.WriteTo(stream); err != nil {
		return 0, err
	}

	return int64(5 * binary.Size(uint64(0))), err
}

// ReadFrom reads a binary representation of BloomFilter (such as might
// have been written by WriteTo()) from an i/o stream. It returns the number
// of bytes read.
func (b *BloomFilter) ReadFrom(stream io.Reader) (int64, error) {
	var count, m, k uint64
	var buckets Buckets

	err := binary.Read(stream, binary.BigEndian, &count)
	if err != nil {
		return 0, err
	}
	err = binary.Read(stream, binary.BigEndian, &m)
	if err != nil {
		return 0, err
	}
	err = binary.Read(stream, binary.BigEndian, &k)
	if err != nil {
		return 0, err
	}

	buckets.ReadFrom(stream)
	b.count = uint(count)
	b.m = uint(m)
	b.k = uint(k)
	b.buckets = &buckets
	return int64(5 * binary.Size(uint64(0))), nil
}

// GobEncode implements gob.GobEncoder interface.
func (b *BloomFilter) GobEncode() ([]byte, error) {
	var buf bytes.Buffer
	_, err := b.WriteTo(&buf)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// GobDecode implements gob.GobDecoder interface.
func (b *BloomFilter) GobDecode(data []byte) error {
	buf := bytes.NewBuffer(data)
	_, err := b.ReadFrom(buf)

	return err
}
<commit_msg>Fixing return sizes<commit_after>package boom

import (
	"bytes"
	"encoding/binary"
	"hash"
	"hash/fnv"
	"io"
	"math"
)

// BloomFilter implements a classic Bloom filter. A Bloom filter has a non-zero
// probability of false positives and a zero probability of false negatives.
type BloomFilter struct {
	buckets *Buckets    // filter data
	hash    hash.Hash64 // hash function (kernel for all k functions)
	m       uint        // filter size
	k       uint        // number of hash functions
	count   uint        // number of items added
}

// NewBloomFilter creates a new Bloom filter optimized to store n items with a
// specified target false-positive rate.
func NewBloomFilter(n uint, fpRate float64) *BloomFilter {
	m := OptimalM(n, fpRate)
	return &BloomFilter{
		buckets: NewBuckets(m, 1),
		hash:    fnv.New64(),
		m:       m,
		k:       OptimalK(fpRate),
	}
}

// Capacity returns the Bloom filter capacity, m.
func (b *BloomFilter) Capacity() uint {
	return b.m
}

// K returns the number of hash functions.
func (b *BloomFilter) K() uint {
	return b.k
}

// Count returns the number of items added to the filter.
func (b *BloomFilter) Count() uint {
	return b.count
}

// EstimatedFillRatio returns the current estimated ratio of set bits.
func (b *BloomFilter) EstimatedFillRatio() float64 {
	return 1 - math.Exp((-float64(b.count)*float64(b.k))/float64(b.m))
}

// FillRatio returns the ratio of set bits.
func (b *BloomFilter) FillRatio() float64 {
	sum := uint32(0)
	for i := uint(0); i < b.buckets.Count(); i++ {
		sum += b.buckets.Get(i)
	}
	return float64(sum) / float64(b.m)
}

// Test will test for membership of the data and returns true if it is a
// member, false if not. This is a probabilistic test, meaning there is a
// non-zero probability of false positives but a zero probability of false
// negatives.
func (b *BloomFilter) Test(data []byte) bool {
	lower, upper := hashKernel(data, b.hash)

	// If any of the K bits are not set, then it's not a member.
	for i := uint(0); i < b.k; i++ {
		if b.buckets.Get((uint(lower)+uint(upper)*i)%b.m) == 0 {
			return false
		}
	}

	return true
}

// Add will add the data to the Bloom filter. It returns the filter to allow
// for chaining.
func (b *BloomFilter) Add(data []byte) Filter {
	lower, upper := hashKernel(data, b.hash)

	// Set the K bits.
	for i := uint(0); i < b.k; i++ {
		b.buckets.Set((uint(lower)+uint(upper)*i)%b.m, 1)
	}

	b.count++
	return b
}

// TestAndAdd is equivalent to calling Test followed by Add. It returns true if
// the data is a member, false if not.
func (b *BloomFilter) TestAndAdd(data []byte) bool {
	lower, upper := hashKernel(data, b.hash)
	member := true

	// If any of the K bits are not set, then it's not a member.
	for i := uint(0); i < b.k; i++ {
		idx := (uint(lower) + uint(upper)*i) % b.m
		if b.buckets.Get(idx) == 0 {
			member = false
		}
		b.buckets.Set(idx, 1)
	}

	b.count++
	return member
}

// Reset restores the Bloom filter to its original state. It returns the filter
// to allow for chaining.
func (b *BloomFilter) Reset() *BloomFilter {
	b.buckets.Reset()
	return b
}

// SetHash sets the hashing function used in the filter.
// For the effect on false positive rates see: https://github.com/tylertreat/BoomFilters/pull/1
func (b *BloomFilter) SetHash(h hash.Hash64) {
	b.hash = h
}

// WriteTo writes a binary representation of the BloomFilter to an i/o stream.
// It returns the number of bytes written.
func (b *BloomFilter) WriteTo(stream io.Writer) (int64, error) {
	err := binary.Write(stream, binary.BigEndian, uint64(b.count))
	if err != nil {
		return 0, err
	}
	err = binary.Write(stream, binary.BigEndian, uint64(b.m))
	if err != nil {
		return 0, err
	}
	err = binary.Write(stream, binary.BigEndian, uint64(b.k))
	if err != nil {
		return 0, err
	}

	writtenSize, err := b.buckets.WriteTo(stream)
	if err != nil {
		return 0, err
	}

	return writtenSize + int64(3 * binary.Size(uint64(0))), err
}

// ReadFrom reads a binary representation of BloomFilter (such as might
// have been written by WriteTo()) from an i/o stream. It returns the number
// of bytes read.
func (b *BloomFilter) ReadFrom(stream io.Reader) (int64, error) {
	var count, m, k uint64
	var buckets Buckets

	err := binary.Read(stream, binary.BigEndian, &count)
	if err != nil {
		return 0, err
	}
	err = binary.Read(stream, binary.BigEndian, &m)
	if err != nil {
		return 0, err
	}
	err = binary.Read(stream, binary.BigEndian, &k)
	if err != nil {
		return 0, err
	}

	readSize, err := buckets.ReadFrom(stream)
	if err != nil {
		return 0, err
	}

	b.count = uint(count)
	b.m = uint(m)
	b.k = uint(k)
	b.buckets = &buckets
	return readSize + int64(3 * binary.Size(uint64(0))), nil
}

// GobEncode implements gob.GobEncoder interface.
func (b *BloomFilter) GobEncode() ([]byte, error) {
	var buf bytes.Buffer
	_, err := b.WriteTo(&buf)
	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

// GobDecode implements gob.GobDecoder interface.
func (b *BloomFilter) GobDecode(data []byte) error {
	buf := bytes.NewBuffer(data)
	_, err := b.ReadFrom(buf)

	return err
}
<|endoftext|>


<commit_before>package apidsl

import (
	"fmt"

	"bitbucket.org/pkg/inflect"
	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/dslengine"
)

// Action implements the action definition DSL. Action definitions describe specific API endpoints
// including the URL, HTTP method and request parameters (via path wildcards or query strings) and
// payload (data structure describing the request HTTP body). An action belongs to a resource and
// "inherits" default values from the resource definition including the URL path prefix, default
// response media type and default payload attribute properties (inherited from the attribute with
// identical name in the resource default media type). Action definitions also describe all the
// possible responses including the HTTP status, headers and body. Here is an example showing all
// the possible sub-definitions:
//	Action("Update", func() {
//		Description("Update account")
//		Docs(func() {
//			Description("Update docs")
//			URL("http//cellarapi.com/docs/actions/update")
//		})
//		Scheme("http")
//		Routing(
//			PUT("/:id"),				// Full action path is built by appending "/:id" to parent resource base path
//			PUT("//orgs/:org/accounts/:id"),	// The // prefix indicates an absolute path
//		)
//		Params(func() {					// Params describe the action parameters
//			Param("org", String)			// Parameters may correspond to path wildcards
//			Param("id", Integer)
//			Param("sort", func() {			// or URL query string values.
//				Enum("asc", "desc")
//			})
//		})
//		Headers(func() {				// Headers describe relevant action headers
//			Header("Authorization", String)
//			Header("X-Account", Integer)
//			Required("Authorization", "X-Account")
//		})
//		Payload(UpdatePayload)				// Payload describes the HTTP request body (here using a type)
//		Response(NoContent)				// Each possible HTTP response is described via Response
//		Response(NotFound)
//	})
func Action(name string, dsl func()) {
	if r, ok := resourceDefinition(); ok {
		if r.Actions == nil {
			r.Actions = make(map[string]*design.ActionDefinition)
		}
		action, ok := r.Actions[name]
		if !ok {
			action = &design.ActionDefinition{
				Parent: r,
				Name:   name,
			}
		}
		if !dslengine.Execute(dsl, action) {
			return
		}
		r.Actions[name] = action
	}
}

// Routing lists the action route. Each route is defined with a function named after the HTTP method.
// The route function takes the path as argument. Route paths may use wildcards as described in the
// [httptreemux](https://godoc.org/github.com/dimfeld/httptreemux) package documentation. These
// wildcards define parameters using the `:name` or `*name` syntax where `:name` matches a path
// segment and `*name` is a catch-all that matches the path until the end.
func Routing(routes ...*design.RouteDefinition) {
	if a, ok := actionDefinition(); ok {
		for _, r := range routes {
			r.Parent = a
			a.Routes = append(a.Routes, r)
		}
	}
}

// GET creates a route using the GET HTTP method.
func GET(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "GET", Path: path}
}

// HEAD creates a route using the HEAD HTTP method.
func HEAD(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "HEAD", Path: path}
}

// POST creates a route using the POST HTTP method.
func POST(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "POST", Path: path}
}

// PUT creates a route using the PUT HTTP method.
func PUT(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "PUT", Path: path}
}

// DELETE creates a route using the DELETE HTTP method.
func DELETE(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "DELETE", Path: path}
}

// OPTIONS creates a route using the OPTIONS HTTP method.
func OPTIONS(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "OPTIONS", Path: path}
}

// TRACE creates a route using the TRACE HTTP method.
func TRACE(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "TRACE", Path: path}
}

// CONNECT creates a route using the GET HTTP method.
func CONNECT(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "CONNECT", Path: path}
}

// PATCH creates a route using the PATCH HTTP method.
func PATCH(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "PATCH", Path: path}
}

// Headers implements the DSL for describing HTTP headers. The DSL syntax is identical to the one
// of Attribute. Here is an example defining a couple of headers with validations:
//
//	Headers(func() {
//		Header("Authorization")
//		Header("X-Account", Integer, func() {
//			Minimum(1)
//		})
//		Required("Authorization")
//	})
//
// Headers can be used inside Action to define the action request headers, Response to define the
// response headers or Resource to define common request headers to all the resource actions.
func Headers(dsl func()) {
	switch def := dslengine.CurrentDefinition().(type) {
	case *design.ActionDefinition:
		headers := newAttribute(def.Parent.MediaType)
		if dslengine.Execute(dsl, headers) {
			def.Headers = headers
		}

	case *design.ResourceDefinition:
		headers := newAttribute(def.MediaType)
		if dslengine.Execute(dsl, headers) {
			def.Headers = headers
		}

	case *design.ResponseDefinition:
		if def.Headers != nil {
			dslengine.ReportError("headers already defined")
			return
		}
		var h *design.AttributeDefinition
		switch actual := def.Parent.(type) {
		case *design.ResourceDefinition:
			h = newAttribute(actual.MediaType)
		case *design.ActionDefinition:
			h = newAttribute(actual.Parent.MediaType)
		case nil: // API ResponseTemplate
			h = &design.AttributeDefinition{}
		default:
			dslengine.ReportError("invalid use of Response or ResponseTemplate")
		}
		if dslengine.Execute(dsl, h) {
			def.Headers = h
		}

	default:
		dslengine.IncompatibleDSL()
	}
}

// Params describe the action parameters, either path parameters identified via wildcards or query
// string parameters. Each parameter is described via the `Param` function which uses the same DSL
// as the Attribute DSL. Here is an example:
//
//	Params(func() {
//		Param("id", Integer)		// A path parameter defined using e.g. GET("/:id")
//		Param("sort", String, func() {	// A query string parameter
//			Enum("asc", "desc")
//		})
//	})
//
// Params can be used inside Action to define the action parameters or Resource to define common
// parameters to all the resource actions.
func Params(dsl func()) {
	switch def := dslengine.CurrentDefinition().(type) {
	case *design.ActionDefinition:
		params := newAttribute(def.Parent.MediaType)
		params.Type = make(design.Object)
		if dslengine.Execute(dsl, params) {
			def.Params = params
		}

	case *design.ResourceDefinition:
		params := newAttribute(def.MediaType)
		params.Type = make(design.Object)
		if dslengine.Execute(dsl, params) {
			def.Params = params
		}

	default:
		dslengine.IncompatibleDSL()
	}
}

// Payload implements the action payload DSL. An action payload describes the HTTP request body
// data structure. The function accepts either a type or a DSL that describes the payload members
// using the Member DSL which accepts the same syntax as the Attribute DSL. This function can be
// called passing in a type, a DSL or both. Examples:
//
//	Payload(BottlePayload)		// Request payload is described by the BottlePayload type
//
//	Payload(func() {		// Request payload is an object and is described inline
//		Member("Name")
//	})
//
//	Payload(BottlePayload, func() {	// Request payload is described by merging the inline
//		Required("Name")	// definition into the BottlePayload type.
//	})
//
func Payload(p interface{}, dsls ...func()) {
	if len(dsls) > 1 {
		dslengine.ReportError("too many arguments given to Payload")
		return
	}
	if a, ok := actionDefinition(); ok {
		var att *design.AttributeDefinition
		var dsl func()
		switch actual := p.(type) {
		case func():
			dsl = actual
			att = newAttribute(a.Parent.MediaType)
			att.Type = design.Object{}
		case *design.AttributeDefinition:
			att = design.DupAtt(actual)
		case design.DataStructure:
			att = design.DupAtt(actual.Definition())
		case string:
			ut, ok := design.Design.Types[actual]
			if !ok {
				dslengine.ReportError("unknown payload type %s", actual)
			}
			att = design.DupAtt(ut.AttributeDefinition)
		case *design.Array:
			att = &design.AttributeDefinition{Type: actual}
		case *design.Hash:
			att = &design.AttributeDefinition{Type: actual}
		case design.Primitive:
			att = &design.AttributeDefinition{Type: actual}
		}
		if len(dsls) == 1 {
			if dsl != nil {
				dslengine.ReportError("invalid arguments in Payload call, must be (type), (dsl) or (type, dsl)")
			}
			dsl = dsls[0]
		}
		if dsl != nil {
			dslengine.Execute(dsl, att)
		}
		rn := inflect.Camelize(a.Parent.Name)
		an := inflect.Camelize(a.Name)
		a.Payload = &design.UserTypeDefinition{
			AttributeDefinition: att,
			TypeName:            fmt.Sprintf("%s%sPayload", an, rn),
		}
	}
}

// newAttribute creates a new attribute definition using the media type with the given identifier
// as base type.
func newAttribute(baseMT string) *design.AttributeDefinition {
	var base design.DataType
	if mt := design.Design.MediaTypeWithIdentifier(baseMT); mt != nil {
		base = mt.Type
	}
	return &design.AttributeDefinition{Reference: base}
}
<commit_msg>Make Payload more robust.<commit_after>package apidsl

import (
	"fmt"

	"bitbucket.org/pkg/inflect"
	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/dslengine"
)

// Action implements the action definition DSL. Action definitions describe specific API endpoints
// including the URL, HTTP method and request parameters (via path wildcards or query strings) and
// payload (data structure describing the request HTTP body). An action belongs to a resource and
// "inherits" default values from the resource definition including the URL path prefix, default
// response media type and default payload attribute properties (inherited from the attribute with
// identical name in the resource default media type). Action definitions also describe all the
// possible responses including the HTTP status, headers and body. Here is an example showing all
// the possible sub-definitions:
//	Action("Update", func() {
//		Description("Update account")
//		Docs(func() {
//			Description("Update docs")
//			URL("http//cellarapi.com/docs/actions/update")
//		})
//		Scheme("http")
//		Routing(
//			PUT("/:id"),				// Full action path is built by appending "/:id" to parent resource base path
//			PUT("//orgs/:org/accounts/:id"),	// The // prefix indicates an absolute path
//		)
//		Params(func() {					// Params describe the action parameters
//			Param("org", String)			// Parameters may correspond to path wildcards
//			Param("id", Integer)
//			Param("sort", func() {			// or URL query string values.
//				Enum("asc", "desc")
//			})
//		})
//		Headers(func() {				// Headers describe relevant action headers
//			Header("Authorization", String)
//			Header("X-Account", Integer)
//			Required("Authorization", "X-Account")
//		})
//		Payload(UpdatePayload)				// Payload describes the HTTP request body (here using a type)
//		Response(NoContent)				// Each possible HTTP response is described via Response
//		Response(NotFound)
//	})
func Action(name string, dsl func()) {
	if r, ok := resourceDefinition(); ok {
		if r.Actions == nil {
			r.Actions = make(map[string]*design.ActionDefinition)
		}
		action, ok := r.Actions[name]
		if !ok {
			action = &design.ActionDefinition{
				Parent: r,
				Name:   name,
			}
		}
		if !dslengine.Execute(dsl, action) {
			return
		}
		r.Actions[name] = action
	}
}

// Routing lists the action route. Each route is defined with a function named after the HTTP method.
// The route function takes the path as argument. Route paths may use wildcards as described in the
// [httptreemux](https://godoc.org/github.com/dimfeld/httptreemux) package documentation. These
// wildcards define parameters using the `:name` or `*name` syntax where `:name` matches a path
// segment and `*name` is a catch-all that matches the path until the end.
func Routing(routes ...*design.RouteDefinition) {
	if a, ok := actionDefinition(); ok {
		for _, r := range routes {
			r.Parent = a
			a.Routes = append(a.Routes, r)
		}
	}
}

// GET creates a route using the GET HTTP method.
func GET(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "GET", Path: path}
}

// HEAD creates a route using the HEAD HTTP method.
func HEAD(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "HEAD", Path: path}
}

// POST creates a route using the POST HTTP method.
func POST(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "POST", Path: path}
}

// PUT creates a route using the PUT HTTP method.
func PUT(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "PUT", Path: path}
}

// DELETE creates a route using the DELETE HTTP method.
func DELETE(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "DELETE", Path: path}
}

// OPTIONS creates a route using the OPTIONS HTTP method.
func OPTIONS(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "OPTIONS", Path: path}
}

// TRACE creates a route using the TRACE HTTP method.
func TRACE(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "TRACE", Path: path}
}

// CONNECT creates a route using the GET HTTP method.
func CONNECT(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "CONNECT", Path: path}
}

// PATCH creates a route using the PATCH HTTP method.
func PATCH(path string) *design.RouteDefinition {
	return &design.RouteDefinition{Verb: "PATCH", Path: path}
}

// Headers implements the DSL for describing HTTP headers. The DSL syntax is identical to the one
// of Attribute. Here is an example defining a couple of headers with validations:
//
//	Headers(func() {
//		Header("Authorization")
//		Header("X-Account", Integer, func() {
//			Minimum(1)
//		})
//		Required("Authorization")
//	})
//
// Headers can be used inside Action to define the action request headers, Response to define the
// response headers or Resource to define common request headers to all the resource actions.
func Headers(dsl func()) {
	switch def := dslengine.CurrentDefinition().(type) {
	case *design.ActionDefinition:
		headers := newAttribute(def.Parent.MediaType)
		if dslengine.Execute(dsl, headers) {
			def.Headers = headers
		}

	case *design.ResourceDefinition:
		headers := newAttribute(def.MediaType)
		if dslengine.Execute(dsl, headers) {
			def.Headers = headers
		}

	case *design.ResponseDefinition:
		if def.Headers != nil {
			dslengine.ReportError("headers already defined")
			return
		}
		var h *design.AttributeDefinition
		switch actual := def.Parent.(type) {
		case *design.ResourceDefinition:
			h = newAttribute(actual.MediaType)
		case *design.ActionDefinition:
			h = newAttribute(actual.Parent.MediaType)
		case nil: // API ResponseTemplate
			h = &design.AttributeDefinition{}
		default:
			dslengine.ReportError("invalid use of Response or ResponseTemplate")
		}
		if dslengine.Execute(dsl, h) {
			def.Headers = h
		}

	default:
		dslengine.IncompatibleDSL()
	}
}

// Params describe the action parameters, either path parameters identified via wildcards or query
// string parameters. Each parameter is described via the `Param` function which uses the same DSL
// as the Attribute DSL. Here is an example:
//
//	Params(func() {
//		Param("id", Integer)		// A path parameter defined using e.g. GET("/:id")
//		Param("sort", String, func() {	// A query string parameter
//			Enum("asc", "desc")
//		})
//	})
//
// Params can be used inside Action to define the action parameters or Resource to define common
// parameters to all the resource actions.
func Params(dsl func()) {
	switch def := dslengine.CurrentDefinition().(type) {
	case *design.ActionDefinition:
		params := newAttribute(def.Parent.MediaType)
		params.Type = make(design.Object)
		if dslengine.Execute(dsl, params) {
			def.Params = params
		}

	case *design.ResourceDefinition:
		params := newAttribute(def.MediaType)
		params.Type = make(design.Object)
		if dslengine.Execute(dsl, params) {
			def.Params = params
		}

	default:
		dslengine.IncompatibleDSL()
	}
}

// Payload implements the action payload DSL. An action payload describes the HTTP request body
// data structure. The function accepts either a type or a DSL that describes the payload members
// using the Member DSL which accepts the same syntax as the Attribute DSL. This function can be
// called passing in a type, a DSL or both. Examples:
//
//	Payload(BottlePayload)		// Request payload is described by the BottlePayload type
//
//	Payload(func() {		// Request payload is an object and is described inline
//		Member("Name")
//	})
//
//	Payload(BottlePayload, func() {	// Request payload is described by merging the inline
//		Required("Name")	// definition into the BottlePayload type.
//	})
//
func Payload(p interface{}, dsls ...func()) {
	if len(dsls) > 1 {
		dslengine.ReportError("too many arguments given to Payload")
		return
	}
	if a, ok := actionDefinition(); ok {
		var att *design.AttributeDefinition
		var dsl func()
		switch actual := p.(type) {
		case func():
			dsl = actual
			att = newAttribute(a.Parent.MediaType)
			att.Type = design.Object{}
		case *design.AttributeDefinition:
			att = design.DupAtt(actual)
		case design.DataStructure:
			att = design.DupAtt(actual.Definition())
		case string:
			ut, ok := design.Design.Types[actual]
			if !ok {
				dslengine.ReportError("unknown payload type %s", actual)
			}
			att = design.DupAtt(ut.AttributeDefinition)
		case *design.Array:
			att = &design.AttributeDefinition{Type: actual}
		case *design.Hash:
			att = &design.AttributeDefinition{Type: actual}
		case design.Primitive:
			att = &design.AttributeDefinition{Type: actual}
		default:
			dslengine.ReportError("invalid Payload argument, must be a type, a media type or a DSL building a type")
			return
		}
		if len(dsls) == 1 {
			if dsl != nil {
				dslengine.ReportError("invalid arguments in Payload call, must be (type), (dsl) or (type, dsl)")
			}
			dsl = dsls[0]
		}
		if dsl != nil {
			dslengine.Execute(dsl, att)
		}
		rn := inflect.Camelize(a.Parent.Name)
		an := inflect.Camelize(a.Name)
		a.Payload = &design.UserTypeDefinition{
			AttributeDefinition: att,
			TypeName:            fmt.Sprintf("%s%sPayload", an, rn),
		}
	}
}

// newAttribute creates a new attribute definition using the media type with the given identifier
// as base type.
func newAttribute(baseMT string) *design.AttributeDefinition {
	var base design.DataType
	if mt := design.Design.MediaTypeWithIdentifier(baseMT); mt != nil {
		base = mt.Type
	}
	return &design.AttributeDefinition{Reference: base}
}
<|endoftext|>


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

package cli

import (
	"os"

	"github.com/coreos/pkg/capnslog"
	"github.com/spf13/cobra"

	"github.com/coreos/mantle/system/exec"
	"github.com/coreos/mantle/version"
)

var (
	versionCmd = &cobra.Command{
		Use:   "version",
		Short: "Print the version number and exit.",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Printf("mantle/%s version %s\n",
				cmd.Root().Name(), version.Version)
		},
	}

	logDebug   bool
	logVerbose bool
	logLevel   = capnslog.NOTICE

	plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "cli")
)

// Execute sets up common features that all mantle commands should share
// and then executes the command. It does not return.
func Execute(main *cobra.Command) {
	// If we were invoked via a multicall entrypoint run it instead.
	// TODO(marineam): should we figure out a way to initialize logging?
	exec.MaybeExec()

	main.AddCommand(versionCmd)

	// TODO(marineam): pflags defines the Value interface differently,
	// update capnslog accordingly...
	//main.PersistentFlags().Var(&level, "log-level",
	//	"Set global log level. (default is NOTICE)")
	main.PersistentFlags().BoolVarP(&logVerbose, "verbose", "v", false,
		"Alias for --log-level=INFO")
	main.PersistentFlags().BoolVarP(&logDebug, "debug", "d", false,
		"Alias for --log-level=DEBUG")

	var preRun = main.PersistentPreRun
	main.PersistentPreRun = func(cmd *cobra.Command, args []string) {
		startLogging(cmd)
		if preRun != nil {
			preRun(cmd, args)
		}
	}

	if err := main.Execute(); err != nil {
		plog.Fatal(err)
	}
	os.Exit(0)
}

func setRepoLogLevel(repo string, l capnslog.LogLevel) {
	r, err := capnslog.GetRepoLogger(repo)
	if err != nil {
		return // don't care if it isn't linked in
	}
	r.SetRepoLogLevel(l)
}

func startLogging(cmd *cobra.Command) {
	switch {
	case logDebug:
		logLevel = capnslog.DEBUG
	case logVerbose:
		logLevel = capnslog.INFO
	}

	capnslog.SetFormatter(capnslog.NewStringFormatter(cmd.Out()))
	capnslog.SetGlobalLogLevel(logLevel)

	// In the context of the internally linked etcd, the NOTICE messages
	// aren't really interesting, so translate NOTICE to WARNING instead.
	if logLevel == capnslog.NOTICE {
		// etcd sure has a lot of repos in its repo
		setRepoLogLevel("github.com/coreos/etcd", capnslog.WARNING)
		setRepoLogLevel("github.com/coreos/etcd/etcdserver", capnslog.WARNING)
		setRepoLogLevel("github.com/coreos/etcd/etcdserver/etcdhttp", capnslog.WARNING)
		setRepoLogLevel("github.com/coreos/etcd/pkg", capnslog.WARNING)
	}

	plog.Infof("Started logging at level %s", logLevel)
}
<commit_msg>cli: re-add --log-level command line option<commit_after>// Copyright 2014 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cli

import (
	"os"

	"github.com/coreos/pkg/capnslog"
	"github.com/spf13/cobra"

	"github.com/coreos/mantle/system/exec"
	"github.com/coreos/mantle/version"
)

var (
	versionCmd = &cobra.Command{
		Use:   "version",
		Short: "Print the version number and exit.",
		Run: func(cmd *cobra.Command, args []string) {
			cmd.Printf("mantle/%s version %s\n",
				cmd.Root().Name(), version.Version)
		},
	}

	logDebug   bool
	logVerbose bool
	logLevel   = capnslog.NOTICE

	plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "cli")
)

// Execute sets up common features that all mantle commands should share
// and then executes the command. It does not return.
func Execute(main *cobra.Command) {
	// If we were invoked via a multicall entrypoint run it instead.
	// TODO(marineam): should we figure out a way to initialize logging?
	exec.MaybeExec()

	main.AddCommand(versionCmd)

	// TODO(marineam): pflags defines the Value interface differently,
	// update capnslog accordingly...
	main.PersistentFlags().Var(&logLevel, "log-level",
		"Set global log level.")
	main.PersistentFlags().BoolVarP(&logVerbose, "verbose", "v", false,
		"Alias for --log-level=INFO")
	main.PersistentFlags().BoolVarP(&logDebug, "debug", "d", false,
		"Alias for --log-level=DEBUG")

	var preRun = main.PersistentPreRun
	main.PersistentPreRun = func(cmd *cobra.Command, args []string) {
		startLogging(cmd)
		if preRun != nil {
			preRun(cmd, args)
		}
	}

	if err := main.Execute(); err != nil {
		plog.Fatal(err)
	}
	os.Exit(0)
}

func setRepoLogLevel(repo string, l capnslog.LogLevel) {
	r, err := capnslog.GetRepoLogger(repo)
	if err != nil {
		return // don't care if it isn't linked in
	}
	r.SetRepoLogLevel(l)
}

func startLogging(cmd *cobra.Command) {
	switch {
	case logDebug:
		logLevel = capnslog.DEBUG
	case logVerbose:
		logLevel = capnslog.INFO
	}

	capnslog.SetFormatter(capnslog.NewStringFormatter(cmd.Out()))
	capnslog.SetGlobalLogLevel(logLevel)

	// In the context of the internally linked etcd, the NOTICE messages
	// aren't really interesting, so translate NOTICE to WARNING instead.
	if logLevel == capnslog.NOTICE {
		// etcd sure has a lot of repos in its repo
		setRepoLogLevel("github.com/coreos/etcd", capnslog.WARNING)
		setRepoLogLevel("github.com/coreos/etcd/etcdserver", capnslog.WARNING)
		setRepoLogLevel("github.com/coreos/etcd/etcdserver/etcdhttp", capnslog.WARNING)
		setRepoLogLevel("github.com/coreos/etcd/pkg", capnslog.WARNING)
	}

	plog.Infof("Started logging at level %s", logLevel)
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"os"
	"path"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

func initLogFormatter() {
	log.SetFormatter(&log.TextFormatter{
		FullTimestamp:   true,
		ForceColors:     true,
		TimestampFormat: "15:04:05",
	})
}

func before(c *cli.Context) error {
	initLogFormatter()

	// Log level
	logLevelStr := c.String(LogLevelKey)
	if logLevelStr == "" {
		logLevelStr = "info"
	}

	level, err := log.ParseLevel(logLevelStr)
	if err != nil {
		return err
	}
	log.SetLevel(level)

	return nil
}

func printVersion(c *cli.Context) {
	fmt.Fprintf(c.App.Writer, "%v\n", c.App.Version)
}

// Run ...
func Run() {
	cli.VersionPrinter = printVersion

	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Usage = ""
	app.Version = "0.9.2"

	app.Author = ""
	app.Email = ""

	app.Before = before

	app.Flags = flags
	app.Commands = commands

	if err := app.Run(os.Args); err != nil {
		log.Fatal("Finished with Error:", err)
	}
}
<commit_msg>start of v0.9.3<commit_after>package cli

import (
	"fmt"
	"os"
	"path"

	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
)

func initLogFormatter() {
	log.SetFormatter(&log.TextFormatter{
		FullTimestamp:   true,
		ForceColors:     true,
		TimestampFormat: "15:04:05",
	})
}

func before(c *cli.Context) error {
	initLogFormatter()

	// Log level
	logLevelStr := c.String(LogLevelKey)
	if logLevelStr == "" {
		logLevelStr = "info"
	}

	level, err := log.ParseLevel(logLevelStr)
	if err != nil {
		return err
	}
	log.SetLevel(level)

	return nil
}

func printVersion(c *cli.Context) {
	fmt.Fprintf(c.App.Writer, "%v\n", c.App.Version)
}

// Run ...
func Run() {
	cli.VersionPrinter = printVersion

	app := cli.NewApp()
	app.Name = path.Base(os.Args[0])
	app.Usage = ""
	app.Version = "0.9.3"

	app.Author = ""
	app.Email = ""

	app.Before = before

	app.Flags = flags
	app.Commands = commands

	if err := app.Run(os.Args); err != nil {
		log.Fatal("Finished with Error:", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"runtime"
	"strings"
)

//MrRedisFW IP and Port number
var MrRedisFW string

//Init simply loads the scheduler's endpoint from the .MrRedis before performing any subcommand
func Init() {
	var confFilePath string
	if runtime.GOOS == "windows" {
		confFilePath = ".MrRedis"
	} else {
		confFilePath = "/tmp/.MrRedis"
	}

	//Check if we have a ~/.MrRedis config file in the system already,
	f, err := os.Open(conf_file_path)
	if err != nil {
		fmt.Printf("Cli is not initalized err=%v\n", err)
		fmt.Printf("$mrr init <http://MrRedisEndPoint>\n")
		return
	}
	//If yes then open it and read the content (first line)
	d := make([]byte, 512)
	if c, err := f.Read(d); err != nil && c != 0 {
		fmt.Printf("Unable to read the config file err=%v\n", err)
		return
	}
	//It should have IP:Port format
	//MrRedisFW = string(d)
	MrRedisFW = strings.Trim(string(d), "\x00")
	f.Close()
	return
}

func main() {
	Init()
	app := cli.NewApp()
	app.Name = "mrr"
	app.Usage = "MrRedis Command Line Interface"
	app.HideVersion = true
	app.Action = func(c *cli.Context) {
		println("MrRedis Command Line")
	}

	app.Commands = []cli.Command{
		{
			Name:    "init",
			Aliases: []string{"i"},
			Usage:   "$mrr init <http://MrRedisEndPoint>",
			Action:  InitCmd,
		},
		{
			Name:    "create",
			Aliases: []string{"c"},
			Usage:   "Create a Redis Instance",
			Action:  CreateCmd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "name, n",
					Usage: "Name of the Redis Instance",
				},
				cli.IntFlag{
					Name:  "memory, m",
					Usage: "Memory in MB",
				},
				cli.IntFlag{
					Name:  "slaves, s",
					Usage: "Number of Slaves",
				},
				cli.BoolFlag{
					Name:  "wait, w",
					Usage: "Wait for the Instance to be created (by default the command is async)",
				},
			},
		},
		{
			Name:    "status",
			Aliases: []string{"s"},
			Usage:   "Status of a Redis Instance",
			Action:  StatusCmd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "name, n",
					Usage: "Name of the Redis Instance",
				},
			},
		},
		{
			Name:    "delete",
			Aliases: []string{"d"},
			Usage:   "Delete a Redis Instance",
			Action:  DeleteCmd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "name, n",
					Usage: "Name of the Redis Instance",
				},
			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>fixed #28 (#29)<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"runtime"
	"strings"
)

//MrRedisFW IP and Port number
var MrRedisFW string

//Init simply loads the scheduler's endpoint from the .MrRedis before performing any subcommand
func Init() {
	var confFilePath string
	if runtime.GOOS == "windows" {
		confFilePath = ".MrRedis"
	} else {
		confFilePath = "/tmp/.MrRedis"
	}

	//Check if we have a ~/.MrRedis config file in the system already,
	f, err := os.Open(confFilePath)
	if err != nil {
		fmt.Printf("Cli is not initalized err=%v\n", err)
		fmt.Printf("$mrr init <http://MrRedisEndPoint>\n")
		return
	}
	//If yes then open it and read the content (first line)
	d := make([]byte, 512)
	if c, err := f.Read(d); err != nil && c != 0 {
		fmt.Printf("Unable to read the config file err=%v\n", err)
		return
	}
	//It should have IP:Port format
	//MrRedisFW = string(d)
	MrRedisFW = strings.Trim(string(d), "\x00")
	f.Close()
	return
}

func main() {
	Init()
	app := cli.NewApp()
	app.Name = "mrr"
	app.Usage = "MrRedis Command Line Interface"
	app.HideVersion = true
	app.Action = func(c *cli.Context) {
		println("MrRedis Command Line")
	}

	app.Commands = []cli.Command{
		{
			Name:    "init",
			Aliases: []string{"i"},
			Usage:   "$mrr init <http://MrRedisEndPoint>",
			Action:  InitCmd,
		},
		{
			Name:    "create",
			Aliases: []string{"c"},
			Usage:   "Create a Redis Instance",
			Action:  CreateCmd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "name, n",
					Usage: "Name of the Redis Instance",
				},
				cli.IntFlag{
					Name:  "memory, m",
					Usage: "Memory in MB",
				},
				cli.IntFlag{
					Name:  "slaves, s",
					Usage: "Number of Slaves",
				},
				cli.BoolFlag{
					Name:  "wait, w",
					Usage: "Wait for the Instance to be created (by default the command is async)",
				},
			},
		},
		{
			Name:    "status",
			Aliases: []string{"s"},
			Usage:   "Status of a Redis Instance",
			Action:  StatusCmd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "name, n",
					Usage: "Name of the Redis Instance",
				},
			},
		},
		{
			Name:    "delete",
			Aliases: []string{"d"},
			Usage:   "Delete a Redis Instance",
			Action:  DeleteCmd,
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:  "name, n",
					Usage: "Name of the Redis Instance",
				},
			},
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package air

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"path"
	"reflect"
	"runtime"
	"sync"
)

type (
	// Air is the top-level framework struct.
	Air struct {
		pregases    []Gas
		gases       []Gas
		paramCap    int
		contextPool *sync.Pool
		server      *server
		router      *router

		Config           *Config
		Logger           Logger
		Binder           Binder
		Minifier         Minifier
		Renderer         Renderer
		Coffer           Coffer
		HTTPErrorHandler HTTPErrorHandler
	}

	// Handler defines a function to serve HTTP requests.
	Handler func(*Context) error

	// Gas defines a function to process gases.
	Gas func(Handler) Handler

	// HTTPError represents an error that occurred while handling an HTTP request.
	HTTPError struct {
		Code    int
		Message string
	}

	// HTTPErrorHandler is a centralized HTTP error handler.
	HTTPErrorHandler func(error, *Context)

	// Map is the `map[string]interface{}`.
	Map map[string]interface{}
)

// MIME types
const (
	MIMEApplicationJSON               = "application/json"
	MIMEApplicationJavaScript         = "application/javascript"
	MIMEApplicationXML                = "application/xml"
	MIMEApplicationXWWWFormURLEncoded = "application/x-www-form-urlencoded"
	MIMEImageJPEG                     = "image/jpeg"
	MIMEImagePNG                      = "image/png"
	MIMEImageSVGXML                   = "image/svg+xml"
	MIMEMultipartFormData             = "multipart/form-data"
	MIMETextCSS                       = "text/css"
	MIMETextHTML                      = "text/html"
	MIMETextJavaScript                = "text/javascript"
	MIMETextPlain                     = "text/plain"
	MIMETextXML                       = "text/xml"

	CharsetUTF8 = "; charset=utf-8"
)

// HTTP methods
const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT"
	DELETE = "DELETE"
)

// For easy for-range
var methods = [4]string{GET, POST, PUT, DELETE}

// HTTP headers
const (
	HeaderAccept                          = "Accept"
	HeaderAcceptCharset                   = "Accept-Charset"
	HeaderAcceptEncoding                  = "Accept-Encoding"
	HeaderAcceptLanguage                  = "Accept-Language"
	HeaderAcceptRanges                    = "Accept-Ranges"
	HeaderAccessControlAllowCredentials   = "Access-Control-Allow-Credentials"
	HeaderAccessControlAllowHeaders       = "Access-Control-Allow-Headers"
	HeaderAccessControlAllowMethods       = "Access-Control-Allow-Methods"
	HeaderAccessControlAllowOrigin        = "Access-Control-Allow-Origin"
	HeaderAccessControlExposeHeaders      = "Access-Control-Expose-Headers"
	HeaderAccessControlMaxAge             = "Access-Control-Max-Age"
	HeaderAccessControlRequestHeaders     = "Access-Control-Request-Headers"
	HeaderAccessControlRequestMethod      = "Access-Control-Request-Method"
	HeaderAge                             = "Age"
	HeaderAllow                           = "Allow"
	HeaderAuthorization                   = "Authorization"
	HeaderCacheControl                    = "Cache-Control"
	HeaderConnection                      = "Connection"
	HeaderContentDisposition              = "Content-Disposition"
	HeaderContentEncoding                 = "Content-Encoding"
	HeaderContentLanguage                 = "Content-Language"
	HeaderContentLength                   = "Content-Length"
	HeaderContentLocation                 = "Content-Location"
	HeaderContentSecurityPolicy           = "Content-Security-Policy"
	HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
	HeaderContentType                     = "Content-Type"
	HeaderCookie                          = "Cookie"
	HeaderDNT                             = "DNT"
	HeaderDate                            = "Date"
	HeaderETag                            = "ETag"
	HeaderExpires                         = "Expires"
	HeaderForm                            = "Form"
	HeaderHost                            = "Host"
	HeaderIfMatch                         = "If-Match"
	HeaderIfModifiedSince                 = "If-Modified-Since"
	HeaderIfNoneMatch                     = "If-None-Match"
	HeaderIfRange                         = "If-Range"
	HeaderIfUnmodifiedSince               = "If-Unmodified-Since"
	HeaderKeepAlive                       = "Keep-Alive"
	HeaderLastModified                    = "Last-Modified"
	HeaderLocation                        = "Location"
	HeaderOrigin                          = "Origin"
	HeaderPublicKeyPins                   = "Public-Key-Pins"
	HeaderPublicKeyPinsReportOnly         = "Public-Key-Pins-Report-Only"
	HeaderReferer                         = "Referer"
	HeaderReferrerPolicy                  = "Referrer-Policy"
	HeaderRetryAfter                      = "Retry-After"
	HeaderServer                          = "Server"
	HeaderSetCookie                       = "Set-Cookie"
	HeaderStrictTransportSecurity         = "Strict-Transport-Security"
	HeaderTE                              = "TE"
	HeaderTK                              = "TK"
	HeaderTrailer                         = "Trailer"
	HeaderTransferEncoding                = "Transfer-Encoding"
	HeaderUpgrade                         = "Upgrade"
	HeaderUpgradeInsecureRequests         = "Upgrade-Insecure-Requests"
	HeaderUserAgent                       = "User-Agent"
	HeaderVary                            = "Vary"
	HeaderVia                             = "Via"
	HeaderWWWAuthenticate                 = "WWW-Authenticate"
	HeaderWarning                         = "Warning"
	HeaderXCSRFToken                      = "X-CSRF-Token"
	HeaderXContentTypeOptions             = "X-Content-Type-Options"
	HeaderXDNSPrefetchControl             = "X-DNS-Prefetch-Control"
	HeaderXForwardedFor                   = "X-Forwarded-For"
	HeaderXForwardedProto                 = "X-Forwarded-Proto"
	HeaderXFrameOptions                   = "X-Frame-Options"
	HeaderXHTTPMethodOverride             = "X-HTTP-Method-Override"
	HeaderXRealIP                         = "X-Real-IP"
	HeaderXXSSProtection                  = "X-XSS-Protection"
)

// HTTP errors
var (
	ErrUnauthorized                = NewHTTPError(http.StatusUnauthorized)          // 401
	ErrNotFound                    = NewHTTPError(http.StatusNotFound)              // 404
	ErrMethodNotAllowed            = NewHTTPError(http.StatusMethodNotAllowed)      // 405
	ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) // 413
	ErrUnsupportedMediaType        = NewHTTPError(http.StatusUnsupportedMediaType)  // 415

	ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) // 500
	ErrBadGateway          = NewHTTPError(http.StatusBadGateway)          // 502
	ErrServiceUnavailable  = NewHTTPError(http.StatusServiceUnavailable)  // 503
	ErrGatewayTimeout      = NewHTTPError(http.StatusGatewayTimeout)      // 504

	ErrInvalidRedirectCode = errors.New("invalid redirect status code")
)

// HTTP error handlers
var (
	NotFoundHandler = func(c *Context) error {
		return ErrNotFound
	}

	MethodNotAllowedHandler = func(c *Context) error {
		return ErrMethodNotAllowed
	}
)

// New returns a pointer of a new instance of the `Air`.
func New() *Air {
	a := &Air{}

	a.contextPool = &sync.Pool{
		New: func() interface{} {
			return NewContext(a)
		},
	}
	a.server = newServer(a)
	a.router = newRouter(a)

	a.Config = NewConfig("config.toml")
	a.Logger = newLogger(a)
	a.Binder = newBinder()
	a.Minifier = newMinifier()
	a.Renderer = newRenderer(a)
	a.Coffer = newCoffer(a)
	a.HTTPErrorHandler = DefaultHTTPErrorHandler

	return a
}

// Precontain adds the gases to the chain which is perform before the router.
func (a *Air) Precontain(gases ...Gas) {
	a.pregases = append(a.pregases, gases...)
}

// Contain adds the gases to the chain which is perform after the router.
func (a *Air) Contain(gases ...Gas) {
	a.gases = append(a.gases, gases...)
}

// GET registers a new GET route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) GET(path string, h Handler, gases ...Gas) {
	a.add(GET, path, h, gases...)
}

// POST registers a new POST route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) POST(path string, h Handler, gases ...Gas) {
	a.add(POST, path, h, gases...)
}

// PUT registers a new PUT route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) PUT(path string, h Handler, gases ...Gas) {
	a.add(PUT, path, h, gases...)
}

// DELETE registers a new DELETE route for the path with the matching h in the router with the
// optional route-level gases.
func (a *Air) DELETE(path string, h Handler, gases ...Gas) {
	a.add(DELETE, path, h, gases...)
}

// Static registers a new route with the path prefix to serve the static files from the provided
// root directory.
func (a *Air) Static(prefix, root string) {
	a.GET(prefix+"*", func(c *Context) error {
		return c.File(path.Join(root, c.Param("*")))
	})
}

// File registers a new route with the path to serve a static file.
func (a *Air) File(path, file string) {
	a.GET(path, func(c *Context) error {
		return c.File(file)
	})
}

// add registers a new route for the path with the method and the matching h in the router with the
// optional route-level gases.
func (a *Air) add(method, path string, h Handler, gases ...Gas) {
	hn := handlerName(h)

	a.router.add(method, path, func(c *Context) error {
		h := h
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}
		return h(c)
	})

	a.router.routes[method+path] = &route{
		method:  method,
		path:    path,
		handler: hn,
	}
}

// URL returns an URL generated from the h with the optional params.
func (a *Air) URL(h Handler, params ...interface{}) string {
	url := &bytes.Buffer{}
	hn := handlerName(h)
	ln := len(params)
	n := 0

	for _, r := range a.router.routes {
		if r.handler == hn {
			for i, l := 0, len(r.path); i < l; i++ {
				if r.path[i] == ':' && n < ln {
					for ; i < l && r.path[i] != '/'; i++ {
					}
					url.WriteString(fmt.Sprintf("%v", params[n]))
					n++
				}

				if i < l {
					url.WriteByte(r.path[i])
				}
			}

			break
		}
	}

	return url.String()
}

// Serve starts the HTTP server.
func (a *Air) Serve() error {
	if a.Config.DebugMode {
		a.Config.LoggerEnabled = true
		a.Logger.Debug("serving in debug mode")
	}

	go func() {
		if err := a.Minifier.Init(); err != nil {
			a.Logger.Error(err)
		}

		if err := a.Renderer.Init(); err != nil {
			a.Logger.Error(err)
		}

		if err := a.Coffer.Init(); err != nil {
			a.Logger.Error(err)
		}
	}()

	return a.server.serve()
}

// Close closes the HTTP server immediately.
func (a *Air) Close() error {
	return a.server.Close()
}

// Shutdown gracefully shuts down the HTTP server without interrupting any active connections.
func (a *Air) Shutdown(c *Context) error {
	return a.server.Shutdown(c.Context)
}

// handlerName returns the func name of the h.
func handlerName(h Handler) string {
	return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
}

// WrapHandler wraps the h into the `Handler`.
func WrapHandler(h http.Handler) Handler {
	return func(c *Context) error {
		h.ServeHTTP(c.Response, c.Request.Request)
		return nil
	}
}

// WrapGas wraps the h into the `Gas`.
func WrapGas(h Handler) Gas {
	return func(next Handler) Handler {
		return func(c *Context) error {
			if err := h(c); err != nil {
				return err
			}
			return next(c)
		}
	}
}

// NewHTTPError returns a pointer of a new instance of the `HTTPError`.
func NewHTTPError(code int, messages ...interface{}) *HTTPError {
	he := &HTTPError{Code: code, Message: http.StatusText(code)}
	if len(messages) > 0 {
		he.Message = fmt.Sprint(messages...)
	}
	return he
}

// Error implements the `error#Error()`.
func (he *HTTPError) Error() string {
	return he.Message
}

// DefaultHTTPErrorHandler is the default HTTP error handler.
func DefaultHTTPErrorHandler(err error, c *Context) {
	he := ErrInternalServerError
	if che, ok := err.(*HTTPError); ok {
		he = che
	} else if c.Air.Config.DebugMode {
		he.Message = err.Error()
	}

	if !c.Response.Written {
		c.Response.StatusCode = he.Code
		c.String(he.Message)
	}

	c.Air.Logger.Error(err)
}
<commit_msg>refactor: unify HTTP errors<commit_after>package air

import (
	"bytes"
	"errors"
	"fmt"
	"net/http"
	"path"
	"reflect"
	"runtime"
	"sync"
)

type (
	// Air is the top-level framework struct.
	Air struct {
		pregases    []Gas
		gases       []Gas
		paramCap    int
		contextPool *sync.Pool
		server      *server
		router      *router

		Config           *Config
		Logger           Logger
		Binder           Binder
		Minifier         Minifier
		Renderer         Renderer
		Coffer           Coffer
		HTTPErrorHandler HTTPErrorHandler
	}

	// Handler defines a function to serve HTTP requests.
	Handler func(*Context) error

	// Gas defines a function to process gases.
	Gas func(Handler) Handler

	// HTTPError represents an error that occurred while handling an HTTP request.
	HTTPError struct {
		Code    int
		Message string
	}

	// HTTPErrorHandler is a centralized HTTP error handler.
	HTTPErrorHandler func(error, *Context)

	// Map is the `map[string]interface{}`.
	Map map[string]interface{}
)

// MIME types
const (
	MIMEApplicationJSON               = "application/json"
	MIMEApplicationJavaScript         = "application/javascript"
	MIMEApplicationXML                = "application/xml"
	MIMEApplicationXWWWFormURLEncoded = "application/x-www-form-urlencoded"
	MIMEImageJPEG                     = "image/jpeg"
	MIMEImagePNG                      = "image/png"
	MIMEImageSVGXML                   = "image/svg+xml"
	MIMEMultipartFormData             = "multipart/form-data"
	MIMETextCSS                       = "text/css"
	MIMETextHTML                      = "text/html"
	MIMETextJavaScript                = "text/javascript"
	MIMETextPlain                     = "text/plain"
	MIMETextXML                       = "text/xml"

	CharsetUTF8 = "; charset=utf-8"
)

// HTTP methods
const (
	GET    = "GET"
	POST   = "POST"
	PUT    = "PUT"
	DELETE = "DELETE"
)

// For easy for-range
var methods = [4]string{GET, POST, PUT, DELETE}

// HTTP headers
const (
	HeaderAccept                          = "Accept"
	HeaderAcceptCharset                   = "Accept-Charset"
	HeaderAcceptEncoding                  = "Accept-Encoding"
	HeaderAcceptLanguage                  = "Accept-Language"
	HeaderAcceptRanges                    = "Accept-Ranges"
	HeaderAccessControlAllowCredentials   = "Access-Control-Allow-Credentials"
	HeaderAccessControlAllowHeaders       = "Access-Control-Allow-Headers"
	HeaderAccessControlAllowMethods       = "Access-Control-Allow-Methods"
	HeaderAccessControlAllowOrigin        = "Access-Control-Allow-Origin"
	HeaderAccessControlExposeHeaders      = "Access-Control-Expose-Headers"
	HeaderAccessControlMaxAge             = "Access-Control-Max-Age"
	HeaderAccessControlRequestHeaders     = "Access-Control-Request-Headers"
	HeaderAccessControlRequestMethod      = "Access-Control-Request-Method"
	HeaderAge                             = "Age"
	HeaderAllow                           = "Allow"
	HeaderAuthorization                   = "Authorization"
	HeaderCacheControl                    = "Cache-Control"
	HeaderConnection                      = "Connection"
	HeaderContentDisposition              = "Content-Disposition"
	HeaderContentEncoding                 = "Content-Encoding"
	HeaderContentLanguage                 = "Content-Language"
	HeaderContentLength                   = "Content-Length"
	HeaderContentLocation                 = "Content-Location"
	HeaderContentSecurityPolicy           = "Content-Security-Policy"
	HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
	HeaderContentType                     = "Content-Type"
	HeaderCookie                          = "Cookie"
	HeaderDNT                             = "DNT"
	HeaderDate                            = "Date"
	HeaderETag                            = "ETag"
	HeaderExpires                         = "Expires"
	HeaderForm                            = "Form"
	HeaderHost                            = "Host"
	HeaderIfMatch                         = "If-Match"
	HeaderIfModifiedSince                 = "If-Modified-Since"
	HeaderIfNoneMatch                     = "If-None-Match"
	HeaderIfRange                         = "If-Range"
	HeaderIfUnmodifiedSince               = "If-Unmodified-Since"
	HeaderKeepAlive                       = "Keep-Alive"
	HeaderLastModified                    = "Last-Modified"
	HeaderLocation                        = "Location"
	HeaderOrigin                          = "Origin"
	HeaderPublicKeyPins                   = "Public-Key-Pins"
	HeaderPublicKeyPinsReportOnly         = "Public-Key-Pins-Report-Only"
	HeaderReferer                         = "Referer"
	HeaderReferrerPolicy                  = "Referrer-Policy"
	HeaderRetryAfter                      = "Retry-After"
	HeaderServer                          = "Server"
	HeaderSetCookie                       = "Set-Cookie"
	HeaderStrictTransportSecurity         = "Strict-Transport-Security"
	HeaderTE                              = "TE"
	HeaderTK                              = "TK"
	HeaderTrailer                         = "Trailer"
	HeaderTransferEncoding                = "Transfer-Encoding"
	HeaderUpgrade                         = "Upgrade"
	HeaderUpgradeInsecureRequests         = "Upgrade-Insecure-Requests"
	HeaderUserAgent                       = "User-Agent"
	HeaderVary                            = "Vary"
	HeaderVia                             = "Via"
	HeaderWWWAuthenticate                 = "WWW-Authenticate"
	HeaderWarning                         = "Warning"
	HeaderXCSRFToken                      = "X-CSRF-Token"
	HeaderXContentTypeOptions             = "X-Content-Type-Options"
	HeaderXDNSPrefetchControl             = "X-DNS-Prefetch-Control"
	HeaderXForwardedFor                   = "X-Forwarded-For"
	HeaderXForwardedProto                 = "X-Forwarded-Proto"
	HeaderXFrameOptions                   = "X-Frame-Options"
	HeaderXHTTPMethodOverride             = "X-HTTP-Method-Override"
	HeaderXRealIP                         = "X-Real-IP"
	HeaderXXSSProtection                  = "X-XSS-Protection"
)

// HTTP errors
var (
	ErrUnauthorized          = NewHTTPError(http.StatusUnauthorized)          // 401
	ErrNotFound              = NewHTTPError(http.StatusNotFound)              // 404
	ErrMethodNotAllowed      = NewHTTPError(http.StatusMethodNotAllowed)      // 405
	ErrRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge) // 413
	ErrUnsupportedMediaType  = NewHTTPError(http.StatusUnsupportedMediaType)  // 415

	ErrInternalServerError = NewHTTPError(http.StatusInternalServerError) // 500
	ErrBadGateway          = NewHTTPError(http.StatusBadGateway)          // 502
	ErrServiceUnavailable  = NewHTTPError(http.StatusServiceUnavailable)  // 503
	ErrGatewayTimeout      = NewHTTPError(http.StatusGatewayTimeout)      // 504

	ErrInvalidRedirectCode = errors.New("invalid redirect status code")
)

// HTTP error handlers
var (
	NotFoundHandler = func(c *Context) error {
		return ErrNotFound
	}

	MethodNotAllowedHandler = func(c *Context) error {
		return ErrMethodNotAllowed
	}
)

// New returns a pointer of a new instance of the `Air`.
func New() *Air {
	a := &Air{}

	a.contextPool = &sync.Pool{
		New: func() interface{} {
			return NewContext(a)
		},
	}
	a.server = newServer(a)
	a.router = newRouter(a)

	a.Config = NewConfig("config.toml")
	a.Logger = newLogger(a)
	a.Binder = newBinder()
	a.Minifier = newMinifier()
	a.Renderer = newRenderer(a)
	a.Coffer = newCoffer(a)
	a.HTTPErrorHandler = DefaultHTTPErrorHandler

	return a
}

// Precontain adds the gases to the chain which is perform before the router.
func (a *Air) Precontain(gases ...Gas) {
	a.pregases = append(a.pregases, gases...)
}

// Contain adds the gases to the chain which is perform after the router.
func (a *Air) Contain(gases ...Gas) {
	a.gases = append(a.gases, gases...)
}

// GET registers a new GET route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) GET(path string, h Handler, gases ...Gas) {
	a.add(GET, path, h, gases...)
}

// POST registers a new POST route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) POST(path string, h Handler, gases ...Gas) {
	a.add(POST, path, h, gases...)
}

// PUT registers a new PUT route for the path with the matching h in the router with the optional
// route-level gases.
func (a *Air) PUT(path string, h Handler, gases ...Gas) {
	a.add(PUT, path, h, gases...)
}

// DELETE registers a new DELETE route for the path with the matching h in the router with the
// optional route-level gases.
func (a *Air) DELETE(path string, h Handler, gases ...Gas) {
	a.add(DELETE, path, h, gases...)
}

// Static registers a new route with the path prefix to serve the static files from the provided
// root directory.
func (a *Air) Static(prefix, root string) {
	a.GET(prefix+"*", func(c *Context) error {
		return c.File(path.Join(root, c.Param("*")))
	})
}

// File registers a new route with the path to serve a static file.
func (a *Air) File(path, file string) {
	a.GET(path, func(c *Context) error {
		return c.File(file)
	})
}

// add registers a new route for the path with the method and the matching h in the router with the
// optional route-level gases.
func (a *Air) add(method, path string, h Handler, gases ...Gas) {
	hn := handlerName(h)

	a.router.add(method, path, func(c *Context) error {
		h := h
		for i := len(gases) - 1; i >= 0; i-- {
			h = gases[i](h)
		}
		return h(c)
	})

	a.router.routes[method+path] = &route{
		method:  method,
		path:    path,
		handler: hn,
	}
}

// URL returns an URL generated from the h with the optional params.
func (a *Air) URL(h Handler, params ...interface{}) string {
	url := &bytes.Buffer{}
	hn := handlerName(h)
	ln := len(params)
	n := 0

	for _, r := range a.router.routes {
		if r.handler == hn {
			for i, l := 0, len(r.path); i < l; i++ {
				if r.path[i] == ':' && n < ln {
					for ; i < l && r.path[i] != '/'; i++ {
					}
					url.WriteString(fmt.Sprintf("%v", params[n]))
					n++
				}

				if i < l {
					url.WriteByte(r.path[i])
				}
			}

			break
		}
	}

	return url.String()
}

// Serve starts the HTTP server.
func (a *Air) Serve() error {
	if a.Config.DebugMode {
		a.Config.LoggerEnabled = true
		a.Logger.Debug("serving in debug mode")
	}

	go func() {
		if err := a.Minifier.Init(); err != nil {
			a.Logger.Error(err)
		}

		if err := a.Renderer.Init(); err != nil {
			a.Logger.Error(err)
		}

		if err := a.Coffer.Init(); err != nil {
			a.Logger.Error(err)
		}
	}()

	return a.server.serve()
}

// Close closes the HTTP server immediately.
func (a *Air) Close() error {
	return a.server.Close()
}

// Shutdown gracefully shuts down the HTTP server without interrupting any active connections.
func (a *Air) Shutdown(c *Context) error {
	return a.server.Shutdown(c.Context)
}

// handlerName returns the func name of the h.
func handlerName(h Handler) string {
	return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
}

// WrapHandler wraps the h into the `Handler`.
func WrapHandler(h http.Handler) Handler {
	return func(c *Context) error {
		h.ServeHTTP(c.Response, c.Request.Request)
		return nil
	}
}

// WrapGas wraps the h into the `Gas`.
func WrapGas(h Handler) Gas {
	return func(next Handler) Handler {
		return func(c *Context) error {
			if err := h(c); err != nil {
				return err
			}
			return next(c)
		}
	}
}

// NewHTTPError returns a pointer of a new instance of the `HTTPError`.
func NewHTTPError(code int, messages ...interface{}) *HTTPError {
	he := &HTTPError{Code: code, Message: http.StatusText(code)}
	if len(messages) > 0 {
		he.Message = fmt.Sprint(messages...)
	}
	return he
}

// Error implements the `error#Error()`.
func (he *HTTPError) Error() string {
	return he.Message
}

// DefaultHTTPErrorHandler is the default HTTP error handler.
func DefaultHTTPErrorHandler(err error, c *Context) {
	he := ErrInternalServerError
	if che, ok := err.(*HTTPError); ok {
		he = che
	} else if c.Air.Config.DebugMode {
		he.Message = err.Error()
	}

	if !c.Response.Written {
		c.Response.StatusCode = he.Code
		c.String(he.Message)
	}

	c.Air.Logger.Error(err)
}
<|endoftext|>