<commit_before>// Package feature provides a simple mechanism for creating and managing
// feature flags.
//
// This allows code to be dynamically enabled and disabled in
// production, without having to restart or redeploy services.
//
// All functions are concurrency-safe, i.e. can be used from multiple
// goroutines concurrently.
package feature

import (
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"sync"
)

// Set collects a named set of feature flags.
type Set struct {
	mu    sync.RWMutex
	flags map[string]Flag
}

// NewSet returns a new set of feature flags.
func NewSet() *Set {
	return &Set{
		flags: make(map[string]Flag),
	}
}

// Add adds a feature flag to the set.
func (fs *Set) Add(f Flag) error {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	if _, ok := fs.flags[f.Name()]; ok {
		return fmt.Errorf("duplicate feature %q", f.Name())
	}

	fs.flags[f.Name()] = f
	return nil
}

// NewFlag creates a new feature flag with the given name and adds it to
// the feature set.
//
// Returns an error (and does not add the flag) if a flag with
// that name is already contained in the set.
func (fs *Set) NewFlag(name string) (*BooleanFlag, error) {
	f := NewBooleanFlag(name)
	err := fs.Add(f)
	return f, err
}

// Get returns the flag with the given name from the feature set.
//
// Returns nil if no such flag exists.
func (fs *Set) Get(name string) Flag {
	fs.mu.RLock()
	f := fs.flags[name]
	fs.mu.RUnlock()
	return f
}

// ServeHTTP implements an admin interface for the feature set.
//
// The root handler lists all features, and POST
// /feature-name?enabled=true enables the given feature flag.
func (fs *Set) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	lastSlash := strings.LastIndex(req.URL.Path, "/")

	if lastSlash == -1 || lastSlash == len(req.URL.Path)-1 {
		fs.handleIndex(w, req)
		return
	}

	name := req.URL.Path[lastSlash+1:]
	fs.handleFlag(w, req, name)
}

type flagsByName []Flag

func (f flagsByName) Len() int           { return len(f) }
func (f flagsByName) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
func (f flagsByName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }

func (fs *Set) handleIndex(w http.ResponseWriter, req *http.Request) {
	fs.mu.RLock()
	flags := make([]Flag, 0, len(fs.flags))
	for _, f := range fs.flags {
		flags = append(flags, f)
	}
	fs.mu.RUnlock()

	sort.Sort(flagsByName(flags))

	fmt.Fprintf(w, "Flags:\n\n")
	for _, f := range flags {
		switch f := f.(type) {
		case fmt.Stringer:
			fmt.Fprintf(w, "%s\n", f.String())
		default:
			fmt.Fprintf(w, "%s: %v\n", f.Name(), f.IsEnabled())
		}
	}
}

func (fs *Set) handleFlag(w http.ResponseWriter, req *http.Request, name string) {
	flag := fs.Get(name)

	if flag == nil {
		http.Error(w, "no such feature", http.StatusNotFound)
		return
	}

	switch req.Method {
	case "GET":
		switch flag := flag.(type) {
		case fmt.Stringer:
			fmt.Fprintf(w, "%s", flag.String())
		default:
			fmt.Fprintf(w, "%s: %v", name, flag.IsEnabled())
		}
	case "POST":
		err := req.ParseForm()
		if err != nil {
			http.Error(w, "invalid parameters", http.StatusBadRequest)
			return
		}

		if strings.TrimSpace(req.Form.Get("enabled")) == "" {
			if req.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
				req.Form.Set("enabled", "false")
				req.PostForm.Set("enabled", "false")
			}
		}

		switch flag := flag.(type) {
		case SettableFlag:
			err := flag.SetFrom(req.Form)
			if err != nil {
				log.Printf("Error: parsing parameters: %s\n", err)
				http.Error(w, "invalid parameters", http.StatusBadRequest)
			}
		default:
			enabledRaw := req.Form.Get("enabled")
			enabled, err := strconv.ParseBool(enabledRaw)
			if err != nil {
				log.Printf("Error: parsing bool param %q: %s\n", enabledRaw, err)
				http.Error(w, "invalid parameter", http.StatusBadRequest)
				return
			}

			flag.Set(enabled)
		}

		switch flag := flag.(type) {
		case fmt.Stringer:
			fmt.Fprintf(w, "%s", flag.String())
		default:
			fmt.Fprintf(w, "%s: %v", name, flag.IsEnabled())
		}
	default:
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
	}
}

// Flag is an enabled or disabled feature flag.
type Flag interface {
	// Name returns the name of the flag.
	Name() string

	// IsEnabled returns true if the flag is enabled.
	IsEnabled() bool

	// Set enables or disables the flag.
	Set(bool)
}

// BooleanFlag is a feature flag that can be switched on or off.
type BooleanFlag struct {
	name string

	mu      sync.RWMutex
	enabled bool
}

// NewBooleanFlag returns a new boolean feature flag.
func NewBooleanFlag(name string) *BooleanFlag {
	f := &BooleanFlag{
		name: name,

		enabled: false,
	}
	return f
}

// Name returns the name of the flag.
func (f *BooleanFlag) Name() string {
	return f.name
}

// IsEnabled returns true if the flag is enabled.
func (f *BooleanFlag) IsEnabled() bool {
	f.mu.RLock()
	isEnabled := f.enabled
	f.mu.RUnlock()
	return isEnabled
}

// Set enables or disables the flag.
func (f *BooleanFlag) Set(enabled bool) {
	f.mu.Lock()
	f.enabled = enabled
	f.mu.Unlock()
}

type SettableFlag interface {
	SetFrom(url.Values) error
}

func (f *BooleanFlag) SetFrom(vals url.Values) error {
	enabledRaw := vals.Get("enabled")
	if strings.TrimSpace(enabledRaw) == "" {
		return fmt.Errorf("missing 'enabled' parameter")
	}

	enabled, err := strconv.ParseBool(enabledRaw)
	if err != nil {
		return fmt.Errorf("invalid parameter")
	}

	f.Set(enabled)
	return nil
}

// RatioFlag is a feature flag that is only activated for the given ratio
// of invocations.
//
// This allows enabling a feature for only a subset of invocations, for
// example to test a new feature on a smaller scale.
//
// Be aware that you should seed the random number generator from
// math/rand before using this.  This can be done by calling
// `rand.Seed(...)`.
type RatioFlag struct {
	BooleanFlag

	ratio float64
}

// NewRatioFlag returns a new flag that will only activate for the given
// ratio of invocations.
//
// The given ratio must be in the interval [0.0, 1.0).
//
// Note that the flag must also be enabled (via Set()) separately.
func NewRatioFlag(name string, ratio float64) *RatioFlag {
	return &RatioFlag{
		BooleanFlag: BooleanFlag{
			name: name,

			enabled: false,
		},
		ratio: ratio,
	}
}

// IsEnabled returns true if the flag is enabled.
//
// Even when the flag is enabled, only `ratio` of invocations will
// return true.  That is, the flag has to be enabled and only every nth
// invocation (according to ratio) will return true.
func (f *RatioFlag) IsEnabled() bool {
	f.mu.RLock()
	isEnabled := f.enabled
	ratio := f.ratio
	f.mu.RUnlock()

	return isEnabled && rand.Float64() < ratio
}

// SetRatio sets the ratio with which the flag should be activated.
func (f *RatioFlag) SetRatio(r float64) {
	f.mu.Lock()
	f.ratio = r
	f.mu.Unlock()
}

func (f *RatioFlag) String() string {
	f.mu.RLock()
	enabled := f.enabled
	ratio := f.ratio
	f.mu.RUnlock()
	return fmt.Sprintf("%s: %v (ratio=%.2f)", f.name, enabled, ratio)
}

func (f *RatioFlag) SetFrom(vals url.Values) error {
	enabledRaw := vals.Get("enabled")
	if strings.TrimSpace(enabledRaw) != "" {
		enabled, err := strconv.ParseBool(enabledRaw)
		if err != nil {
			return fmt.Errorf("invalid parameter %q", enabledRaw)
		}

		f.Set(enabled)
	}

	ratioRaw := vals.Get("ratio")
	if strings.TrimSpace(ratioRaw) != "" {
		ratio, err := strconv.ParseFloat(ratioRaw, 64)
		if err != nil {
			return fmt.Errorf("invalid parameter %q", ratioRaw)
		}

		f.SetRatio(ratio)
	}

	return nil
}
<commit_msg>Add a simple HTML user interface<commit_after>// Package feature provides a simple mechanism for creating and managing
// feature flags.
//
// This allows code to be dynamically enabled and disabled in
// production, without having to restart or redeploy services.
//
// All functions are concurrency-safe, i.e. can be used from multiple
// goroutines concurrently.
package feature

import (
	"fmt"
	"html/template"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"sync"
)

// Set collects a named set of feature flags.
type Set struct {
	mu    sync.RWMutex
	flags map[string]Flag
}

// NewSet returns a new set of feature flags.
func NewSet() *Set {
	return &Set{
		flags: make(map[string]Flag),
	}
}

// Add adds a feature flag to the set.
func (fs *Set) Add(f Flag) error {
	fs.mu.Lock()
	defer fs.mu.Unlock()

	if _, ok := fs.flags[f.Name()]; ok {
		return fmt.Errorf("duplicate feature %q", f.Name())
	}

	fs.flags[f.Name()] = f
	return nil
}

// NewFlag creates a new feature flag with the given name and adds it to
// the feature set.
//
// Returns an error (and does not add the flag) if a flag with
// that name is already contained in the set.
func (fs *Set) NewFlag(name string) (*BooleanFlag, error) {
	f := NewBooleanFlag(name)
	err := fs.Add(f)
	return f, err
}

// Get returns the flag with the given name from the feature set.
//
// Returns nil if no such flag exists.
func (fs *Set) Get(name string) Flag {
	fs.mu.RLock()
	f := fs.flags[name]
	fs.mu.RUnlock()
	return f
}

// ServeHTTP implements an admin interface for the feature set.
//
// The root handler lists all features, and POST
// /feature-name?enabled=true enables the given feature flag.
func (fs *Set) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	lastSlash := strings.LastIndex(req.URL.Path, "/")

	if lastSlash == -1 || lastSlash == len(req.URL.Path)-1 {
		fs.handleIndex(w, req)
		return
	}

	name := req.URL.Path[lastSlash+1:]
	fs.handleFlag(w, req, name)
}

type flagsByName []Flag

func (f flagsByName) Len() int           { return len(f) }
func (f flagsByName) Swap(i, j int)      { f[i], f[j] = f[j], f[i] }
func (f flagsByName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }

func (fs *Set) handleIndex(w http.ResponseWriter, req *http.Request) {
	fs.mu.RLock()
	flags := make([]Flag, 0, len(fs.flags))
	for _, f := range fs.flags {
		flags = append(flags, f)
	}
	fs.mu.RUnlock()

	sort.Sort(flagsByName(flags))

	if strings.Contains(req.Header.Get("Accept"), "html") {
		fmt.Fprintf(w, `<!doctype html>

		<meta charset="utf-8" />
		<title>Flags</title>

`)
		for _, f := range flags {
			fmt.Fprintf(w, "
")

			switch f := f.(type) {
			case RenderableFlag:
				f.RenderHTML(w)
			default:
				fmt.Fprintf(w, "
%s: %v
\n", f.Name(), f.IsEnabled())
			}

			fmt.Fprintf(w, "\n
\n\n")
		}

		fmt.Fprintf(w, `

`)
	} else {
		fmt.Fprintf(w, "Flags:\n\n")
		for _, f := range flags {
			switch f := f.(type) {
			case fmt.Stringer:
				fmt.Fprintf(w, "%s\n", f.String())
			default:
				fmt.Fprintf(w, "%s: %v\n", f.Name(), f.IsEnabled())
			}
		}
	}
}

func (fs *Set) handleFlag(w http.ResponseWriter, req *http.Request, name string) {
	flag := fs.Get(name)

	if flag == nil {
		http.Error(w, "no such feature", http.StatusNotFound)
		return
	}

	switch req.Method {
	case "GET":
		switch flag := flag.(type) {
		case fmt.Stringer:
			fmt.Fprintf(w, "%s", flag.String())
		default:
			fmt.Fprintf(w, "%s: %v", name, flag.IsEnabled())
		}
	case "POST":
		err := req.ParseForm()
		if err != nil {
			http.Error(w, "invalid parameters", http.StatusBadRequest)
			return
		}

		if strings.TrimSpace(req.Form.Get("enabled")) == "" {
			if req.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
				req.Form.Set("enabled", "false")
				req.PostForm.Set("enabled", "false")
			}
		}

		switch flag := flag.(type) {
		case SettableFlag:
			err := flag.SetFrom(req.Form)
			if err != nil {
				log.Printf("Error: parsing parameters: %s\n", err)
				http.Error(w, "invalid parameters", http.StatusBadRequest)
			}
		default:
			enabledRaw := req.Form.Get("enabled")
			enabled, err := strconv.ParseBool(enabledRaw)
			if err != nil {
				log.Printf("Error: parsing bool param %q: %s\n", enabledRaw, err)
				http.Error(w, "invalid parameter", http.StatusBadRequest)
				return
			}

			flag.Set(enabled)
		}

		referer := req.Header.Get("Referer")
		if referer != "" {
			w.Header().Set("Location", referer)
			w.WriteHeader(http.StatusTemporaryRedirect)
		}

		switch flag := flag.(type) {
		case fmt.Stringer:
			fmt.Fprintf(w, "%s", flag.String())
		default:
			fmt.Fprintf(w, "%s: %v", name, flag.IsEnabled())
		}
	default:
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
	}
}

// Flag is an enabled or disabled feature flag.
type Flag interface {
	// Name returns the name of the flag.
	Name() string

	// IsEnabled returns true if the flag is enabled.
	IsEnabled() bool

	// Set enables or disables the flag.
	Set(bool)
}

// BooleanFlag is a feature flag that can be switched on or off.
type BooleanFlag struct {
	name string

	mu      sync.RWMutex
	enabled bool
}

// NewBooleanFlag returns a new boolean feature flag.
func NewBooleanFlag(name string) *BooleanFlag {
	f := &BooleanFlag{
		name: name,

		enabled: false,
	}
	return f
}

// Name returns the name of the flag.
func (f *BooleanFlag) Name() string {
	return f.name
}

// IsEnabled returns true if the flag is enabled.
func (f *BooleanFlag) IsEnabled() bool {
	f.mu.RLock()
	isEnabled := f.enabled
	f.mu.RUnlock()
	return isEnabled
}

// Set enables or disables the flag.
func (f *BooleanFlag) Set(enabled bool) {
	f.mu.Lock()
	f.enabled = enabled
	f.mu.Unlock()
}

type SettableFlag interface {
	SetFrom(url.Values) error
}

func (f *BooleanFlag) SetFrom(vals url.Values) error {
	enabledRaw := vals.Get("enabled")
	if strings.TrimSpace(enabledRaw) == "" {
		return fmt.Errorf("missing 'enabled' parameter")
	}

	enabled, err := strconv.ParseBool(enabledRaw)
	if err != nil {
		return fmt.Errorf("invalid parameter")
	}

	f.Set(enabled)
	return nil
}

func (f *BooleanFlag) RenderHTML(w http.ResponseWriter) {
	f.mu.RLock()
	enabled := f.enabled
	f.mu.RUnlock()

	err := booleanTmpl.Execute(w, map[string]interface{}{
		"name":    f.Name(),
		"enabled": enabled,
	})
	if err != nil {
		log.Println(err)
	}
}

var booleanTmpl = template.Must(template.New("").Parse(`
<form id="feature-{{ .name }}" class="feature" method="POST" action="./{{ .name }}" autocomplete="off">
	{{ .name }}
	<input name="enabled" type="checkbox" value="true" {{ if .enabled }}checked{{ end }} />

	<input type="submit" value="Apply!" />
</form>`))

// RatioFlag is a feature flag that is only activated for the given ratio
// of invocations.
//
// This allows enabling a feature for only a subset of invocations, for
// example to test a new feature on a smaller scale.
//
// Be aware that you should seed the random number generator from
// math/rand before using this.  This can be done by calling
// `rand.Seed(...)`.
type RatioFlag struct {
	BooleanFlag

	ratio float64
}

// NewRatioFlag returns a new flag that will only activate for the given
// ratio of invocations.
//
// The given ratio must be in the interval [0.0, 1.0).
//
// Note that the flag must also be enabled (via Set()) separately.
func NewRatioFlag(name string, ratio float64) *RatioFlag {
	return &RatioFlag{
		BooleanFlag: BooleanFlag{
			name: name,

			enabled: false,
		},
		ratio: ratio,
	}
}

// IsEnabled returns true if the flag is enabled.
//
// Even when the flag is enabled, only `ratio` of invocations will
// return true.  That is, the flag has to be enabled and only every nth
// invocation (according to ratio) will return true.
func (f *RatioFlag) IsEnabled() bool {
	f.mu.RLock()
	isEnabled := f.enabled
	ratio := f.ratio
	f.mu.RUnlock()

	return isEnabled && rand.Float64() < ratio
}

// SetRatio sets the ratio with which the flag should be activated.
func (f *RatioFlag) SetRatio(r float64) {
	f.mu.Lock()
	f.ratio = r
	f.mu.Unlock()
}

func (f *RatioFlag) String() string {
	f.mu.RLock()
	enabled := f.enabled
	ratio := f.ratio
	f.mu.RUnlock()
	return fmt.Sprintf("%s: %v (ratio=%.2f)", f.name, enabled, ratio)
}

func (f *RatioFlag) SetFrom(vals url.Values) error {
	enabledRaw := vals.Get("enabled")
	if strings.TrimSpace(enabledRaw) != "" {
		enabled, err := strconv.ParseBool(enabledRaw)
		if err != nil {
			return fmt.Errorf("invalid parameter %q", enabledRaw)
		}

		f.Set(enabled)
	}

	ratioRaw := vals.Get("ratio")
	if strings.TrimSpace(ratioRaw) != "" {
		ratio, err := strconv.ParseFloat(ratioRaw, 64)
		if err != nil {
			return fmt.Errorf("invalid parameter %q", ratioRaw)
		}

		f.SetRatio(ratio)
	}

	return nil
}

type RenderableFlag interface {
	RenderHTML(w http.ResponseWriter)
}

func (f *RatioFlag) RenderHTML(w http.ResponseWriter) {
	f.mu.RLock()
	enabled := f.enabled
	ratio := f.ratio
	f.mu.RUnlock()

	err := ratioTmpl.Execute(w, map[string]interface{}{
		"name":         f.Name(),
		"enabled":      enabled,
		"ratio":        ratio,
		"ratioPercent": fmt.Sprintf("%.0f%%", ratio*100),
	})
	if err != nil {
		log.Println(err)
	}
}

var ratioTmpl = template.Must(template.New("").Parse(`
<form id="feature-{{ .name }}" class="feature" method="POST" action="./{{ .name }}" autocomplete="off" oninput="ratio_value.value = Math.round(parseFloat(ratio.value)*100) + '%'">
	{{ .name }}
	<input name="enabled" type="checkbox" value="true"{{ if .enabled }} checked{{ end }} />
	<input name="ratio" type="range" min="0.0" max="1.0" step="0.01" value="{{ .ratio }}" />
	<output name="ratio_value">{{ .ratioPercent }}</output>

	<input type="submit" value="Apply!" />
</form>`))
<|endoftext|>


<commit_before>package show

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/urfave/cli"
)

func init() {
	Commands = append(Commands, cli.Command{
		Name:      "stopped eligible vms",
		Usage:     "shows a list of stopped VMs that should be running",
		UsageText: "--admin show stopped_eligible_vms [--json]",
		Flags:     app.OutputFlags("servers", "array"),
		Action: app.Action(with.Auth, func(c *app.Context) error {
			vms, err := c.Client().GetStoppedEligibleVMs()
			if err != nil {
				return err
			}
			return c.OutputInDesiredForm(vms, output.Table)
		}),
	})
}
<commit_msg>update usage text and description for show waiting servers<commit_after>package show

import (
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/urfave/cli"
)

func init() {
	Commands = append(Commands, cli.Command{
		Name:      "stopped waiting servers",
		Usage:     "shows a list of stopped VMs that should be running",
		UsageText: "--admin show waiting servers [--json]",
		Flags:     app.OutputFlags("servers", "array"),
		Action: app.Action(with.Auth, func(c *app.Context) error {
			vms, err := c.Client().GetStoppedEligibleVMs()
			if err != nil {
				return err
			}
			return c.OutputInDesiredForm(vms, output.Table)
		}),
	})
}
<|endoftext|>


<commit_before>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"fmt"
	"os"
	"time"
)

// This log writer sends output to a file
type FileLogWriter struct {
	rec chan *LogRecord
	rot chan bool

	// The opened file
	filename string
	file     *os.File

	// The logging format
	format string

	// File header/trailer
	header, trailer string

	// Rotate at linecount
	maxlines          int
	maxlines_curlines int

	// Rotate at size
	maxsize         int
	maxsize_cursize int

	// Rotate daily
	daily          bool
	daily_opendate int

	// Keep old logfiles (.001, .002, etc)
	rotate bool
}

// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
	w.rec <- rec
}

func (w *FileLogWriter) Close() {
	close(w.rec)
}

// NewFileLogWriter creates a new LogWriter which writes to the given file and
// has rotation enabled if rotate is true.
//
// If rotate is true, any time a new log file is opened, the old one is renamed
// with a .### extension to preserve it.  The various Set* methods can be used
// to configure log rotation based on lines, size, and daily.
//
// The standard log-line format is:
//   [%D %T] [%L] (%S) %M
func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
	w := &FileLogWriter{
		rec:      make(chan *LogRecord, LogBufferLength),
		rot:      make(chan bool),
		filename: fname,
		format:   "[%D %T] [%L] (%S) %M",
		rotate:   rotate,
	}

	// open the file for the first time
	if err := w.intRotate(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
		return nil
	}

	go func() {
		defer func() {
			if w.file != nil {
				fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
				w.file.Close()
			}
		}()

		for {
			select {
			case <-w.rot:
				if err := w.intRotate(); err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}
			case rec, ok := <-w.rec:
				if !ok {
					return
				}
				now := time.Now()
				if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
					(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
					(w.daily && now.Day() != w.daily_opendate) {
					if err := w.intRotate(); err != nil {
						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
						return
					}
				}

				// Perform the write
				n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
				if err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}

				// Update the counts
				w.maxlines_curlines++
				w.maxsize_cursize += n
			}
		}
	}()

	return w
}

// Request that the logs rotate
func (w *FileLogWriter) Rotate() {
	w.rot <- true
}

// If this is called in a threaded context, it MUST be synchronized
func (w *FileLogWriter) intRotate() error {
	// Close any log file that may be open
	if w.file != nil {
		fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
		w.file.Close()
	}

	// If we are keeping log files, move it to the next available number
	if w.rotate {
		_, err := os.Lstat(w.filename)
		if err == nil { // file exists
			// Find the next available number
			num := 1
			fname := ""
			if w.daily {
				if time.Now().Day() != w.daily_opendate {
					t := time.Now().Add(-24 * time.Hour).Format("2006-01-02")
					fname = w.filename + fmt.Sprintf(".%s", t)
				}
			} else {
				for ; err == nil && num <= 999; num++ {
					fname = w.filename + fmt.Sprintf(".%03d", num)
					_, err = os.Lstat(fname)
				}
			}
			// return error if the last file checked still existed
			if err == nil {
				return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
			}

			// Rename the file to its newfound home
			err = os.Rename(w.filename, fname)
			if err != nil {
				return fmt.Errorf("Rotate: %s\n", err)
			}
		}
	}

	// Open the log file
	fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return err
	}
	w.file = fd

	now := time.Now()
	fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))

	// Set the daily open date to the current date
	w.daily_opendate = now.Day()

	// initialize rotation values
	w.maxlines_curlines = 0
	w.maxsize_cursize = 0

	return nil
}

// Set the logging format (chainable).  Must be called before the first log
// message is written.
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
	w.format = format
	return w
}

// Set the logfile header and footer (chainable).  Must be called before the first log
// message is written.  These are formatted similar to the FormatLogRecord (e.g.
// you can use %D and %T in your header/footer for date and time).
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
	w.header, w.trailer = head, foot
	if w.maxlines_curlines == 0 {
		fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
	}
	return w
}

// Set rotate at linecount (chainable). Must be called before the first log
// message is written.
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
	w.maxlines = maxlines
	return w
}

// Set rotate at size (chainable). Must be called before the first log message
// is written.
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
	w.maxsize = maxsize
	return w
}

// Set rotate daily (chainable). Must be called before the first log message is
// written.
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
	w.daily = daily
	return w
}

// SetRotate changes whether or not the old logs are kept. (chainable) Must be
// called before the first log message is written.  If rotate is false, the
// files are overwritten; otherwise, they are rotated to another file before the
// new log is opened.
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
	w.rotate = rotate
	return w
}

// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
// output XML record log messages instead of line-based ones.
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
	return NewFileLogWriter(fname, rotate).SetFormat(
		`	<record level="%L">
		<timestamp>%D %T</timestamp>
		<source>%S</source>
		<message>%M</message>
	</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
}
<commit_msg>bugfix:加入按日期结尾滚动的时候触发了死锁 	modified:   filelog.go<commit_after>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"fmt"
	"os"
	"time"
)

// This log writer sends output to a file
type FileLogWriter struct {
	rec chan *LogRecord
	rot chan bool

	// The opened file
	filename string
	file     *os.File

	// The logging format
	format string

	// File header/trailer
	header, trailer string

	// Rotate at linecount
	maxlines          int
	maxlines_curlines int

	// Rotate at size
	maxsize         int
	maxsize_cursize int

	// Rotate daily
	daily          bool
	daily_opendate int

	// Keep old logfiles (.001, .002, etc)
	rotate bool
}

// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
	w.rec <- rec
}

func (w *FileLogWriter) Close() {
	close(w.rec)
}

// NewFileLogWriter creates a new LogWriter which writes to the given file and
// has rotation enabled if rotate is true.
//
// If rotate is true, any time a new log file is opened, the old one is renamed
// with a .### extension to preserve it.  The various Set* methods can be used
// to configure log rotation based on lines, size, and daily.
//
// The standard log-line format is:
//   [%D %T] [%L] (%S) %M
func NewFileLogWriter(fname string, rotate bool) *FileLogWriter {
	w := &FileLogWriter{
		rec:      make(chan *LogRecord, LogBufferLength),
		rot:      make(chan bool),
		filename: fname,
		format:   "[%D %T] [%L] (%S) %M",
		rotate:   rotate,
	}

	// open the file for the first time
	if err := w.intRotate(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
		return nil
	}

	go func() {
		defer func() {
			if w.file != nil {
				fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
				w.file.Close()
			}
		}()

		for {
			select {
			case <-w.rot:
				if err := w.intRotate(); err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}
			case rec, ok := <-w.rec:
				if !ok {
					return
				}
				now := time.Now()
				if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
					(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) ||
					(w.daily && now.Day() != w.daily_opendate) {
					if err := w.intRotate(); err != nil {
						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
						return
					}
				}

				// Perform the write
				n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
				if err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}

				// Update the counts
				w.maxlines_curlines++
				w.maxsize_cursize += n
			}
		}
	}()

	return w
}

// Request that the logs rotate
func (w *FileLogWriter) Rotate() {
	w.rot <- true
}

// If this is called in a threaded context, it MUST be synchronized
func (w *FileLogWriter) intRotate() error {
	// Close any log file that may be open
	if w.file != nil {
		fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
		w.file.Close()
	}

	// If we are keeping log files, move it to the next available number
	if w.rotate {
		_, err := os.Lstat(w.filename)
		if err == nil { // file exists
			// Find the next available number
			num := 1
			fname := ""
			if w.daily {
				if time.Now().Day() != w.daily_opendate {
					t := time.Now().Add(-24 * time.Hour).Format("2006-01-02")
					fname = w.filename + fmt.Sprintf(".%s", t)
				}
			} else {
				for ; err == nil && num <= 999; num++ {
					fname = w.filename + fmt.Sprintf(".%03d", num)
					_, err = os.Lstat(fname)
				}
				// return error if the last file checked still existed
				if err == nil {
					return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
				}
			}

			// Rename the file to its newfound home
			err = os.Rename(w.filename, fname)
			if err != nil {
				return fmt.Errorf("Rotate: %s\n", err)
			}
		}
	}

	// Open the log file
	fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return err
	}
	w.file = fd

	now := time.Now()
	fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))

	// Set the daily open date to the current date
	w.daily_opendate = now.Day()

	// initialize rotation values
	w.maxlines_curlines = 0
	w.maxsize_cursize = 0

	return nil
}

// Set the logging format (chainable).  Must be called before the first log
// message is written.
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
	w.format = format
	return w
}

// Set the logfile header and footer (chainable).  Must be called before the first log
// message is written.  These are formatted similar to the FormatLogRecord (e.g.
// you can use %D and %T in your header/footer for date and time).
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
	w.header, w.trailer = head, foot
	if w.maxlines_curlines == 0 {
		fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
	}
	return w
}

// Set rotate at linecount (chainable). Must be called before the first log
// message is written.
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
	w.maxlines = maxlines
	return w
}

// Set rotate at size (chainable). Must be called before the first log message
// is written.
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
	w.maxsize = maxsize
	return w
}

// Set rotate daily (chainable). Must be called before the first log message is
// written.
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
	w.daily = daily
	return w
}

// SetRotate changes whether or not the old logs are kept. (chainable) Must be
// called before the first log message is written.  If rotate is false, the
// files are overwritten; otherwise, they are rotated to another file before the
// new log is opened.
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
	w.rotate = rotate
	return w
}

// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
// output XML record log messages instead of line-based ones.
func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter {
	return NewFileLogWriter(fname, rotate).SetFormat(
		`	<record level="%L">
		<timestamp>%D %T</timestamp>
		<source>%S</source>
		<message>%M</message>
	</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
}
<|endoftext|>


<commit_before>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"fmt"
	"os"
	"time"
)

// This log writer sends output to a file
type FileLogWriter struct {
	rec chan *LogRecord
	rot chan bool

	// The opened file
	filename string
	file     *os.File

	// The logging format
	format string

	// File header/trailer
	header, trailer string

	// Rotate at linecount
	maxlines          int
	maxlines_curlines int

	// Rotate at size
	maxsize         int
	maxsize_cursize int

	// Rotate daily
	daily          bool
	daily_opendate int

	// Keep old logfiles (.001, .002, etc)
	rotate bool
}

// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
	w.rec <- rec
}

func (w *FileLogWriter) Close() {
	close(w.rec)
}

// NewFileLogWriter creates a new LogWriter which writes to the given file and
// has rotation enabled if rotate is true.
//
// If rotate is true, any time a new log file is opened, the old one is renamed
// with a .### extension to preserve it.  The various Set* methods can be used
// to configure log rotation based on lines, size, and daily.
//
// The standard log-line format is:
//   [%D %T] [%L] (%S) %M
func NewFileLogWriter(fname string, rotate bool, daily bool) *FileLogWriter {
	w := &FileLogWriter{
		rec:            make(chan *LogRecord, LogBufferLength),
		rot:            make(chan bool),
		filename:       fname,
		daily_opendate: time.Now().Day(),
		format:         "[%D %T] [%L] (%S) %M",
		rotate:         rotate,
		daily:          daily}

	// open the file for the first time
	if err := w.intRotate(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
		return nil
	}

	go func() {
		defer func() {
			if w.file != nil {
				fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
				w.file.Close()
			}
		}()

		for {
			select {
			case <-w.rot:
				if err := w.intRotate(); err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}
			case rec, ok := <-w.rec:
				if !ok {
					return
				}
				now := time.Now()
				//如果是开启了并且按天滚动，并且已经换了一天需要重建
				if w.daily {
					if now.Day() != w.daily_opendate {
						if err := w.intRotate(); err != nil {
							fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
							return
						}
					}
				} else if !w.daily && ((w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
					(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize)) {
					if err := w.intRotate(); err != nil {
						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
						return
					}
				}

				// Perform the write
				n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
				if err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}

				// Update the counts
				w.maxlines_curlines++
				w.maxsize_cursize += n
			}
		}
	}()

	return w
}

// Request that the logs rotate
func (w *FileLogWriter) Rotate() {
	w.rot <- true
}

// If this is called in a threaded context, it MUST be synchronized
func (w *FileLogWriter) intRotate() error {
	// Close any log file that may be open
	if w.file != nil {
		fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
		w.file.Close()
	}

	// If we are keeping log files, move it to the next available number
	if w.rotate {
		_, err := os.Lstat(w.filename)
		if err == nil { // file exists
			// Find the next available number
			num := 1
			fname := w.filename

			for ; err == nil && num <= 999; num++ {
				if w.daily {
					if time.Now().Day() != w.daily_opendate {
						t := time.Now().Add(-24 * time.Hour).Format("2006-01-02")
						fname = w.filename + fmt.Sprintf(".%s.%3d", t, num)
					}
				} else {
					fname = w.filename + fmt.Sprintf(".%03d", num)
				}

				_, err = os.Lstat(fname)
			}
			// return error if the last file checked still existed
			if err == nil {
				return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
			}

			// Rename the file to its newfound home
			err = os.Rename(w.filename, fname)
			if err != nil {
				return fmt.Errorf("Rotate: %s\n", err)
			}
		}
	}

	// Open the log file
	fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return err
	}
	w.file = fd

	now := time.Now()
	fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))

	// Set the daily open date to the current date
	w.daily_opendate = now.Day()

	// initialize rotation values
	w.maxlines_curlines = 0
	w.maxsize_cursize = 0

	return nil
}

// Set the logging format (chainable).  Must be called before the first log
// message is written.
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
	w.format = format
	return w
}

// Set the logfile header and footer (chainable).  Must be called before the first log
// message is written.  These are formatted similar to the FormatLogRecord (e.g.
// you can use %D and %T in your header/footer for date and time).
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
	w.header, w.trailer = head, foot
	if w.maxlines_curlines == 0 {
		fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
	}
	return w
}

// Set rotate at linecount (chainable). Must be called before the first log
// message is written.
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
	w.maxlines = maxlines
	return w
}

// Set rotate at size (chainable). Must be called before the first log message
// is written.
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
	w.maxsize = maxsize
	return w
}

// Set rotate daily (chainable). Must be called before the first log message is
// written.
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
	w.daily = daily
	return w
}

// SetRotate changes whether or not the old logs are kept. (chainable) Must be
// called before the first log message is written.  If rotate is false, the
// files are overwritten; otherwise, they are rotated to another file before the
// new log is opened.
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
	w.rotate = rotate
	return w
}

// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
// output XML record log messages instead of line-based ones.
func NewXMLLogWriter(fname string, rotate bool, daily bool) *FileLogWriter {
	return NewFileLogWriter(fname, rotate, daily).SetFormat(
		`	<record level="%L">
		<timestamp>%D %T</timestamp>
		<source>%S</source>
		<message>%M</message>
	</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
}
<commit_msg>	modified:   filelog.go<commit_after>// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>.  All rights reserved.

package log4go

import (
	"fmt"
	"os"
	"time"
)

// This log writer sends output to a file
type FileLogWriter struct {
	rec chan *LogRecord
	rot chan bool

	// The opened file
	filename string
	file     *os.File

	// The logging format
	format string

	// File header/trailer
	header, trailer string

	// Rotate at linecount
	maxlines          int
	maxlines_curlines int

	// Rotate at size
	maxsize         int
	maxsize_cursize int

	// Rotate daily
	daily          bool
	daily_opendate int

	// Keep old logfiles (.001, .002, etc)
	rotate bool
}

// This is the FileLogWriter's output method
func (w *FileLogWriter) LogWrite(rec *LogRecord) {
	w.rec <- rec
}

func (w *FileLogWriter) Close() {
	close(w.rec)
}

// NewFileLogWriter creates a new LogWriter which writes to the given file and
// has rotation enabled if rotate is true.
//
// If rotate is true, any time a new log file is opened, the old one is renamed
// with a .### extension to preserve it.  The various Set* methods can be used
// to configure log rotation based on lines, size, and daily.
//
// The standard log-line format is:
//   [%D %T] [%L] (%S) %M
func NewFileLogWriter(fname string, rotate bool, daily bool) *FileLogWriter {
	w := &FileLogWriter{
		rec:            make(chan *LogRecord, LogBufferLength),
		rot:            make(chan bool),
		filename:       fname,
		daily_opendate: time.Now().Day(),
		format:         "[%D %T] [%L] (%S) %M",
		rotate:         rotate,
		daily:          daily}

	// open the file for the first time
	if err := w.intRotate(); err != nil {
		fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
		return nil
	}

	go func() {
		defer func() {
			if w.file != nil {
				fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
				w.file.Close()
			}
		}()

		for {
			select {
			case <-w.rot:
				if err := w.intRotate(); err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}
			case rec, ok := <-w.rec:
				if !ok {
					return
				}
				now := time.Now()
				//如果是开启了并且按天滚动，并且已经换了一天需要重建
				if w.daily {
					if now.Day() != w.daily_opendate {
						if err := w.intRotate(); err != nil {
							fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
							return
						}
					}
				} else if !w.daily && ((w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) ||
					(w.maxsize > 0 && w.maxsize_cursize >= w.maxsize)) {
					if err := w.intRotate(); err != nil {
						fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
						return
					}
				}

				// Perform the write
				n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec))
				if err != nil {
					fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err)
					return
				}

				// Update the counts
				w.maxlines_curlines++
				w.maxsize_cursize += n
			}
		}
	}()

	return w
}

// Request that the logs rotate
func (w *FileLogWriter) Rotate() {
	w.rot <- true
}

// If this is called in a threaded context, it MUST be synchronized
func (w *FileLogWriter) intRotate() error {
	// Close any log file that may be open
	if w.file != nil {
		fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()}))
		w.file.Close()
	}

	// If we are keeping log files, move it to the next available number
	if w.rotate {
		_, err := os.Lstat(w.filename)
		if err == nil { // file exists
			// Find the next available number
			num := 1
			fname := w.filename

			for ; err == nil && num <= 999; num++ {
				if w.daily {
					if time.Now().Day() != w.daily_opendate {
						t := time.Now().Add(-24 * time.Hour).Format("2006-01-02")
						fname = w.filename + fmt.Sprintf(".%s.%03d", t, num)
					} else {
						fname = w.filename + fmt.Sprintf(".%03d", num)
					}
				} else {
					fname = w.filename + fmt.Sprintf(".%03d", num)
				}

				_, err = os.Lstat(fname)
			}
			// return error if the last file checked still existed
			if err == nil {
				return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename)
			}

			// Rename the file to its newfound home
			err = os.Rename(w.filename, fname)
			if err != nil {
				return fmt.Errorf("Rotate: %s\n", err)
			}
		}
	}

	// Open the log file
	fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		return err
	}
	w.file = fd

	now := time.Now()
	fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now}))

	// Set the daily open date to the current date
	w.daily_opendate = now.Day()

	// initialize rotation values
	w.maxlines_curlines = 0
	w.maxsize_cursize = 0

	return nil
}

// Set the logging format (chainable).  Must be called before the first log
// message is written.
func (w *FileLogWriter) SetFormat(format string) *FileLogWriter {
	w.format = format
	return w
}

// Set the logfile header and footer (chainable).  Must be called before the first log
// message is written.  These are formatted similar to the FormatLogRecord (e.g.
// you can use %D and %T in your header/footer for date and time).
func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter {
	w.header, w.trailer = head, foot
	if w.maxlines_curlines == 0 {
		fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()}))
	}
	return w
}

// Set rotate at linecount (chainable). Must be called before the first log
// message is written.
func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines)
	w.maxlines = maxlines
	return w
}

// Set rotate at size (chainable). Must be called before the first log message
// is written.
func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize)
	w.maxsize = maxsize
	return w
}

// Set rotate daily (chainable). Must be called before the first log message is
// written.
func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily)
	w.daily = daily
	return w
}

// SetRotate changes whether or not the old logs are kept. (chainable) Must be
// called before the first log message is written.  If rotate is false, the
// files are overwritten; otherwise, they are rotated to another file before the
// new log is opened.
func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter {
	//fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate)
	w.rotate = rotate
	return w
}

// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to
// output XML record log messages instead of line-based ones.
func NewXMLLogWriter(fname string, rotate bool, daily bool) *FileLogWriter {
	return NewFileLogWriter(fname, rotate, daily).SetFormat(
		`	<record level="%L">
		<timestamp>%D %T</timestamp>
		<source>%S</source>
		<message>%M</message>
	</record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>")
}
<|endoftext|>


<commit_before>package main

import(
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"log"
	"flag"
	"regexp"
	"strings"
	"strconv"
	"path/filepath"
)

var main_colCount = 15
var main_dir string
var main_outHeaderFilename = "out.h"
var main_outHeaderFile *os.File
var main_outModuleFilename = "out.c"
var main_outModuleFile *os.File

func process(fi os.FileInfo) {
	fmt.Println("  processing " + fi.Name())

	f, err := os.Open(main_dir + "/" + fi.Name())
	if err != nil {
		fmt.Println("    error opening file, skipping")
		return
	}
	defer f.Close()

	// Replacing dots to underscores in module name.
	moduleName := strings.ToLower(strings.Replace(strings.TrimSuffix(main_outModuleFilename, filepath.Ext(main_outModuleFilename)), ".", "_", -1))
	moduleVarName := moduleName + "_" + strings.ToLower(strings.Replace(fi.Name(), ".", "_", -1))
	// Replacing spaces to underscores in module variable name.
	moduleVarName = strings.Replace(moduleVarName, " ", "_", -1)
	if _, err := strconv.Atoi(string([]rune(moduleVarName)[0])); err == nil {
		// If the module name starts with a number, we prepend an underscore.
		moduleVarName = "_" + moduleVarName
	}

	arrayDefine := "uint8_t " + moduleVarName + "[" + strconv.FormatInt(fi.Size(), 10) + "]"

	out := "\n" + arrayDefine + " = {\n\t"
	main_outModuleFile.WriteString(out)

	b := make([]byte, 1) // We read to this 1 byte buffer.
	i := 0
	for ; true; i++ {
		bytesRead, err := f.Read(b)
		if bytesRead != 1 {
			if err != io.EOF {
				log.Panic("    error during file read")
				return
			}
			break
		}

		if i > 0 && i < main_colCount {
			main_outModuleFile.WriteString(", ")
		}

		if i == main_colCount {
			main_outModuleFile.WriteString("\n\t")
			i = 0
		}

		out := fmt.Sprintf("0x%.2x", b)
		main_outModuleFile.WriteString(out)
	}
	main_outModuleFile.WriteString("\n};\n")

	main_outHeaderFile.WriteString("extern " + arrayDefine + ";\n")
}

func initHeaderFile() {
	main_outHeaderFile.WriteString("#ifndef " + strings.ToUpper(strings.Replace(main_outHeaderFilename, ".", "_", -1)) + "__\n")
	main_outHeaderFile.WriteString("#define " + strings.ToUpper(strings.Replace(main_outHeaderFilename, ".", "_", -1)) + "__\n\n")
	main_outHeaderFile.WriteString("#include <stdint.h>\n\n")
}

func initModuleFile() {
	main_outModuleFile.WriteString("#include \"" + main_outHeaderFilename + "\"\n");
}

func main() {
	flag.IntVar(&main_colCount, "c", main_colCount, "hex value column count")
	flag.StringVar(&main_dir, "d", main_dir, "convert files in this directory")
	flag.StringVar(&main_outHeaderFilename, "h", main_outHeaderFilename, "output header filename")
	flag.StringVar(&main_outModuleFilename, "m", main_outModuleFilename, "output module filename")
	flag.Parse()

	if main_dir == "" {
		log.Fatal("no convert directory defined, see help")
	}

	var err error
	main_outHeaderFile, err = os.Create(main_outHeaderFilename)
	if err != nil {
		log.Fatal("can't create header file " + main_outHeaderFilename)
	}
	defer main_outHeaderFile.Close()
	initHeaderFile()

	main_outModuleFile, err = os.Create(main_outModuleFilename)
	if err != nil {
		log.Fatal("can't create module file " + main_outModuleFilename)
	}
	defer main_outModuleFile.Close()
	initModuleFile()

	files, err := ioutil.ReadDir(main_dir)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("files2c processing directory " + main_dir)

	// Ignoring .go files.
	regexpGo, _ := regexp.Compile("\\.go$")

    for _, fi := range files {
    	if !fi.Mode().IsRegular() {
    		continue
    	}
    	if fi.Name() == main_outHeaderFilename || fi.Name() == main_outModuleFilename {
    		continue
    	}
    	if regexpGo.MatchString(fi.Name()) {
    		continue
    	}
		process(fi)
	}

	main_outHeaderFile.WriteString("\n#endif\n")

	fmt.Println("files2c done")
}
<commit_msg>Fix commas at line endings<commit_after>package main

import(
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"log"
	"flag"
	"regexp"
	"strings"
	"strconv"
	"path/filepath"
)

var main_colCount = 15
var main_dir string
var main_outHeaderFilename = "out.h"
var main_outHeaderFile *os.File
var main_outModuleFilename = "out.c"
var main_outModuleFile *os.File

func process(fi os.FileInfo) {
	fmt.Println("  processing " + fi.Name())

	f, err := os.Open(main_dir + "/" + fi.Name())
	if err != nil {
		fmt.Println("    error opening file, skipping")
		return
	}
	defer f.Close()

	// Replacing dots to underscores in module name.
	moduleName := strings.ToLower(strings.Replace(strings.TrimSuffix(main_outModuleFilename, filepath.Ext(main_outModuleFilename)), ".", "_", -1))
	moduleVarName := moduleName + "_" + strings.ToLower(strings.Replace(fi.Name(), ".", "_", -1))
	// Replacing spaces to underscores in module variable name.
	moduleVarName = strings.Replace(moduleVarName, " ", "_", -1)
	if _, err := strconv.Atoi(string([]rune(moduleVarName)[0])); err == nil {
		// If the module name starts with a number, we prepend an underscore.
		moduleVarName = "_" + moduleVarName
	}

	arrayDefine := "uint8_t " + moduleVarName + "[" + strconv.FormatInt(fi.Size(), 10) + "]"

	out := "\n" + arrayDefine + " = {\n\t"
	main_outModuleFile.WriteString(out)

	b := make([]byte, 1) // We read to this 1 byte buffer.
	i := 0
	for ; true; i++ {
		bytesRead, err := f.Read(b)
		if bytesRead != 1 {
			if err != io.EOF {
				log.Panic("    error during file read")
				return
			}
			break
		}

		if i > 0 {
			if i < main_colCount {
				main_outModuleFile.WriteString(", ")
			} else {
				main_outModuleFile.WriteString(",")
			}
		}

		if i == main_colCount {
			main_outModuleFile.WriteString("\n\t")
			i = 0
		}

		out := fmt.Sprintf("0x%.2x", b)
		main_outModuleFile.WriteString(out)
	}
	main_outModuleFile.WriteString("\n};\n")

	main_outHeaderFile.WriteString("extern " + arrayDefine + ";\n")
}

func initHeaderFile() {
	main_outHeaderFile.WriteString("#ifndef " + strings.ToUpper(strings.Replace(main_outHeaderFilename, ".", "_", -1)) + "__\n")
	main_outHeaderFile.WriteString("#define " + strings.ToUpper(strings.Replace(main_outHeaderFilename, ".", "_", -1)) + "__\n\n")
	main_outHeaderFile.WriteString("#include <stdint.h>\n\n")
}

func initModuleFile() {
	main_outModuleFile.WriteString("#include \"" + main_outHeaderFilename + "\"\n");
}

func main() {
	flag.IntVar(&main_colCount, "c", main_colCount, "hex value column count")
	flag.StringVar(&main_dir, "d", main_dir, "convert files in this directory")
	flag.StringVar(&main_outHeaderFilename, "h", main_outHeaderFilename, "output header filename")
	flag.StringVar(&main_outModuleFilename, "m", main_outModuleFilename, "output module filename")
	flag.Parse()

	if main_dir == "" {
		log.Fatal("no convert directory defined, see help")
	}

	var err error
	main_outHeaderFile, err = os.Create(main_outHeaderFilename)
	if err != nil {
		log.Fatal("can't create header file " + main_outHeaderFilename)
	}
	defer main_outHeaderFile.Close()
	initHeaderFile()

	main_outModuleFile, err = os.Create(main_outModuleFilename)
	if err != nil {
		log.Fatal("can't create module file " + main_outModuleFilename)
	}
	defer main_outModuleFile.Close()
	initModuleFile()

	files, err := ioutil.ReadDir(main_dir)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("files2c processing directory " + main_dir)

	// Ignoring .go files.
	regexpGo, _ := regexp.Compile("\\.go$")

    for _, fi := range files {
    	if !fi.Mode().IsRegular() {
    		continue
    	}
    	if fi.Name() == main_outHeaderFilename || fi.Name() == main_outModuleFilename {
    		continue
    	}
    	if regexpGo.MatchString(fi.Name()) {
    		continue
    	}
		process(fi)
	}

	main_outHeaderFile.WriteString("\n#endif\n")

	fmt.Println("files2c done")
}
<|endoftext|>


<commit_before>// Package exec provides the entry point of the daemon sub-program and helpers
// to spawn a daemon process.
package daemon

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
)

// Daemon keeps configurations for the daemon sub-program. It can be used both
// from the main function for running the daemon and from another process
// (typically the first Elvish shell session) for spawning a daemon.
type Daemon struct {
	// Forked is the number of times the daemon has forked itself.
	Forked int
	// BinPath is the path to the Elvish binary itself, used when forking.
	BinPath string
	// DbPath is the path to the database.
	DbPath string
	// SockPath is the path to the socket on which the daemon will serve
	// requests.
	SockPath string
	// LogPathPrefix is used to derive the name of the log file by adding the
	// pid.
	LogPathPrefix string
}

// Main is the entry point of the daemon sub-program. It takes a serve function
// and returns the exit status. The caller can call os.Exit after doing more
// cleanup work. The exact behavior of this function depends on the -forked
// flag:
//
// When -forked is 0 (which is how the daemon gets started), it forks another
// daemon process, with an empty environment, the working directory changed to
// /, with umask 0077, and in a new process session. All the path flags are
// resolved to absolute paths, and the -fork flag becomes 1.
//
// When -forked is 1, it simply forks another daemon process with all arguments
// passed as is, except -forked which becomes 2.
//
// When -forked is 2, it calls the serve function with the value of -sockpath
// and -dbpath.
//
// These 3 steps implement a standard demonizing procedure. The main deviation
// is that since Go does not support a raw fork call, it has to use fork-exec to
// simulate fork and pass all states via command line flags.
func (d *Daemon) Main(serve func(string, string)) error {
	switch d.Forked {
	case 0:
		var absifyError error
		absify := func(name string, pvalue *string) {
			if absifyError == nil {
				return
			}
			if *pvalue == "" {
				absifyError = fmt.Errorf("flag %s is required for daemon", name)
				return
			}
			newvalue, err := filepath.Abs(*pvalue)
			if err != nil {
				absifyError = fmt.Errorf("cannot convert %s to absolute path: %s", name, err)
			} else {
				*pvalue = newvalue
			}
		}
		absify("-bin", &d.BinPath)
		absify("-db", &d.DbPath)
		absify("-sock", &d.SockPath)
		absify("-logprefix", &d.LogPathPrefix)
		if absifyError != nil {
			return absifyError
		}

		setUmask()
		return d.fork(
			&exec.Cmd{
				Dir:         "/", // cd to /
				Env:         nil, // empty environment
				SysProcAttr: sysProAttrForFirstFork(),
			})
	case 1:
		return d.fork(nil)
	case 2:
		serve(d.SockPath, d.DbPath)
		return nil
	default:
		return fmt.Errorf("-forked is %d, should be 0, 1 or 2", d.Forked)
	}
}

// Spawn spawns a daemon in the background by calling the Elvish binary with
// appropriate flags. If d.BinPath is not set, it attempts to derive the binary
// path first. The fields DbPath, SockPath and LogPathPrefix must be set.
//
// It is supposed to be called from a Elvish shell (as opposed to daemon) process.
func (d *Daemon) Spawn() error {
	binPath := d.BinPath
	// Determine binPath.
	if binPath == "" {
		bin, err := os.Executable()
		if err != nil {
			return errors.New("cannot find elvish: " + err.Error())
		}
		binPath = bin
	}

	return setArgs(
		nil,
		0,
		binPath,
		d.DbPath,
		d.SockPath,
		d.LogPathPrefix,
	).Run()
}

// fork forks a daemon. It is supposed to be called from the daemon.
func (d *Daemon) fork(cmd *exec.Cmd) error {
	err := setArgs(
		cmd,
		d.Forked+1,
		d.BinPath,
		d.DbPath,
		d.SockPath,
		d.LogPathPrefix,
	).Start()
	return err
}

func setArgs(cmd *exec.Cmd, forkLevel int, binPath, dbPath, sockPath,
	logPathPrefix string) *exec.Cmd {

	if cmd == nil {
		cmd = &exec.Cmd{}
	}

	cmd.Path = binPath
	cmd.Args = []string{
		binPath,
		"-daemon",
		"-forked", strconv.Itoa(forkLevel),
		"-bin", binPath,
		"-db", dbPath,
		"-sock", sockPath,
		"-logprefix", logPathPrefix,
	}

	return cmd
}
<commit_msg>daemon: Cleanup and bugfix.<commit_after>// Package exec provides the entry point of the daemon sub-program and helpers
// to spawn a daemon process.
package daemon

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
)

// Daemon keeps configurations for the daemon sub-program. It can be used both
// from the main function for running the daemon and from another process
// (typically the first Elvish shell session) for spawning a daemon.
type Daemon struct {
	// Forked is the number of times the daemon has forked itself. It is only
	// relevant in the daemon sub-program.
	Forked int
	// BinPath is the path to the Elvish binary itself, used when forking. This
	// field is optional only when spawning the daemon.
	BinPath string
	// DbPath is the path to the database.
	DbPath string
	// SockPath is the path to the socket on which the daemon will serve
	// requests.
	SockPath string
	// LogPathPrefix is used to derive the name of the log file by adding the
	// pid.
	LogPathPrefix string
}

// Main is the entry point of the daemon sub-program. It takes a serve function
// and returns the exit status. The caller can call os.Exit after doing more
// cleanup work. The exact behavior of this function depends on the -forked
// flag:
//
// When d.Forked = 0 (which is how the daemon gets started), it forks another
// daemon process, with an empty environment, the working directory changed to
// /, with umask 0077, and in a new process session. All the path flags are
// resolved to absolute paths, and the -fork flag becomes 1.
//
// When d.Forked = 1, it simply forks another daemon process with all arguments
// passed as is, except -forked which becomes 2.
//
// When d.Forked = 2, it calls the serve function with the value of d.SockPath
// and d.DbPath.
//
// These 3 steps implement a standard demonizing procedure. The main deviation
// is that since Go does not support a raw fork call, it has to use fork-exec to
// simulate fork and pass all states via command line flags.
func (d *Daemon) Main(serve func(string, string)) error {
	switch d.Forked {
	case 0:
		var absifyError error
		absify := func(name string, path string) string {
			if absifyError != nil {
				return ""
			}
			if path == "" {
				absifyError = fmt.Errorf("flag %s is required for daemon", name)
				return ""
			}
			absPath, err := filepath.Abs(path)
			if err != nil {
				absifyError = fmt.Errorf("cannot convert %s to absolute path: %s", name, err)
			}
			return absPath
		}
		binPath := absify("-bin", d.BinPath)
		dbPath := absify("-db", d.DbPath)
		sockPath := absify("-sock", d.SockPath)
		logPathPrefix := absify("-logprefix", d.LogPathPrefix)
		if absifyError != nil {
			return absifyError
		}

		setUmask()
		return startProcess(
			binPath, 1,
			dbPath, sockPath, logPathPrefix,
			&os.ProcAttr{
				Dir: "/",        // cd to /
				Env: []string{}, // empty environment
				Sys: sysProAttrForFirstFork(),
			})
	case 1:
		return startProcess(
			d.BinPath, 2,
			d.DbPath, d.SockPath, d.LogPathPrefix, nil)
	case 2:
		serve(d.SockPath, d.DbPath)
		return nil
	default:
		return fmt.Errorf("-forked is %d, should be 0, 1 or 2", d.Forked)
	}
}

// Spawn spawns a daemon in the background by calling the Elvish binary with
// appropriate flags. If d.BinPath is not set, it attempts to derive the binary
// path using os.Executable(). The fields DbPath, SockPath and LogPathPrefix
// must be set.
//
// It is supposed to be called from a Elvish shell (as opposed to daemon) process.
func (d *Daemon) Spawn() error {
	binPath := d.BinPath
	// Determine binPath.
	if binPath == "" {
		bin, err := os.Executable()
		if err != nil {
			return errors.New("cannot find elvish: " + err.Error())
		}
		binPath = bin
	}

	return startProcess(
		binPath, 0,
		d.DbPath, d.SockPath, d.LogPathPrefix, nil)
}

func startProcess(binPath string, forked int,
	dbPath, sockPath, logPathPrefix string, attr *os.ProcAttr) error {

	if attr == nil {
		attr = &os.ProcAttr{}
	}
	args := []string{
		binPath,
		"-daemon",
		"-forked", strconv.Itoa(forked),
		"-bin", binPath,
		"-db", dbPath,
		"-sock", sockPath,
		"-logprefix", logPathPrefix,
	}

	_, err := os.StartProcess(binPath, args, attr)
	return err
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/scootdev/scoot/common/thrifthelpers"
	s "github.com/scootdev/scoot/saga"
	"github.com/scootdev/scoot/sched"
	"github.com/scootdev/scoot/scootapi/gen-go/scoot"
	"github.com/scootdev/scoot/workerapi/gen-go/worker"
)

func GetJobStatus(jobId string, sc s.SagaCoordinator) (*scoot.JobStatus, error) {
	state, err := sc.GetSagaState(jobId)

	if err != nil {
		js := scoot.NewJobStatus()
		js.ID = ""
		js.Status = scoot.Status_NOT_STARTED

		if err != nil {
			switch err.(type) {
			case s.InvalidRequestError:
				err = scoot.NewInvalidRequest()
			case s.InternalLogError:
				err = scoot.NewScootServerError()
			}
		}

		return js, err
	}

	// No Logged Saga Messages.  Job NotStarted yet
	if state == nil {
		js := scoot.NewJobStatus()
		js.ID = jobId
		js.Status = scoot.Status_NOT_STARTED

		return js, nil
	}

	return convertSagaStateToJobStatus(state), nil
}

// Converts a SagaState to a corresponding JobStatus
func convertSagaStateToJobStatus(sagaState *s.SagaState) *scoot.JobStatus {

	js := scoot.NewJobStatus()

	js.ID = sagaState.SagaId()
	js.Status = scoot.Status_NOT_STARTED
	js.TaskStatus = make(map[string]scoot.Status)
	js.TaskData = make(map[string]*scoot.RunStatus)
	if job, err := sched.DeserializeJob(sagaState.Job()); err == nil {
		for id, _ := range job.Def.Tasks {
			js.TaskStatus[id] = scoot.Status_NOT_STARTED
		}
	}

	// NotStarted Tasks will not have a logged value
	for _, id := range sagaState.GetTaskIds() {

		taskStatus := scoot.Status_NOT_STARTED

		if sagaState.IsSagaAborted() {
			if sagaState.IsCompTaskCompleted(id) {
				taskStatus = scoot.Status_ROLLED_BACK
			} else if sagaState.IsTaskStarted(id) {
				taskStatus = scoot.Status_ROLLING_BACK
			}
		} else {
			if sagaState.IsTaskCompleted(id) {
				taskStatus = scoot.Status_COMPLETED
				if thriftJobStatus, err := workerRunStatusToScootRunStatus(sagaState.GetEndTaskData(id)); err == nil {
					js.TaskData[id] = thriftJobStatus
				}
			} else if sagaState.IsTaskStarted(id) {
				taskStatus = scoot.Status_IN_PROGRESS
				if startData := sagaState.GetStartTaskData(id); startData != nil {
					if thriftJobStatus, err := workerRunStatusToScootRunStatus(startData); err == nil {
						js.TaskData[id] = thriftJobStatus
					}
				}

			}
		}

		js.TaskStatus[id] = taskStatus
	}

	// Saga Completed Successfully
	if sagaState.IsSagaCompleted() && !sagaState.IsSagaAborted() {
		js.Status = scoot.Status_COMPLETED

		// Saga Completed Unsuccessfully was Aborted & Rolled Back
	} else if sagaState.IsSagaCompleted() && sagaState.IsSagaAborted() {
		js.Status = scoot.Status_ROLLED_BACK

		// Saga In Progress
	} else if !sagaState.IsSagaCompleted() && !sagaState.IsSagaAborted() {
		js.Status = scoot.Status_IN_PROGRESS

		// Saga in Progress - Aborted and Rolling Back
	} else if !sagaState.IsSagaCompleted() && sagaState.IsSagaAborted() {
		js.Status = scoot.Status_ROLLING_BACK
	}

	return js
}

// this is a thrift to thrift structure translation.  We are doing this because we get invalid
// import statements in the generated code when we use thrift import statements (this issue is supposed
// to be fixed in thrift 10.0
func workerRunStatusToScootRunStatus(resultsFromSaga []byte) (*scoot.RunStatus, error) {
	if resultsFromSaga == nil {
		return nil, nil
	}

	workerRunStatus := worker.RunStatus{}
	thrifthelpers.JsonDeserialize(&workerRunStatus, resultsFromSaga)

	status, err := scoot.RunStatusStateFromString(workerRunStatus.Status.String())
	if err != nil {
		return nil, err
	}

	scootRunStatus := scoot.RunStatus{
		RunId:      workerRunStatus.RunId,
		Status:     status,
		OutUri:     workerRunStatus.OutUri,
		ErrUri:     workerRunStatus.ErrUri,
		ExitCode:   workerRunStatus.ExitCode,
		Error:      workerRunStatus.Error,
		SnapshotId: workerRunStatus.SnapshotId,
	}

	return &scootRunStatus, nil
}
<commit_msg>scootapi/job_status - initialize TaskData and TaskStatus fields when job is not started (#239)<commit_after>package server

import (
	"github.com/scootdev/scoot/common/thrifthelpers"
	s "github.com/scootdev/scoot/saga"
	"github.com/scootdev/scoot/sched"
	"github.com/scootdev/scoot/scootapi/gen-go/scoot"
	"github.com/scootdev/scoot/workerapi/gen-go/worker"
)

func GetJobStatus(jobId string, sc s.SagaCoordinator) (*scoot.JobStatus, error) {
	state, err := sc.GetSagaState(jobId)

	if err != nil {
		js := scoot.NewJobStatus()
		js.ID = ""
		js.Status = scoot.Status_NOT_STARTED

		switch err.(type) {
		case s.InvalidRequestError:
			err = scoot.NewInvalidRequest()
		case s.InternalLogError:
			err = scoot.NewScootServerError()
		}

		return js, err
	}

	// No Logged Saga Messages.  Job NotStarted yet
	if state == nil {
		js := scoot.NewJobStatus()
		js.ID = jobId
		js.Status = scoot.Status_NOT_STARTED
		js.TaskStatus = make(map[string]scoot.Status)
		js.TaskData = make(map[string]*scoot.RunStatus)

		return js, nil
	}

	return convertSagaStateToJobStatus(state), nil
}

// Converts a SagaState to a corresponding JobStatus
func convertSagaStateToJobStatus(sagaState *s.SagaState) *scoot.JobStatus {

	js := scoot.NewJobStatus()

	js.ID = sagaState.SagaId()
	js.Status = scoot.Status_NOT_STARTED
	js.TaskStatus = make(map[string]scoot.Status)
	js.TaskData = make(map[string]*scoot.RunStatus)
	if job, err := sched.DeserializeJob(sagaState.Job()); err == nil {
		for id, _ := range job.Def.Tasks {
			js.TaskStatus[id] = scoot.Status_NOT_STARTED
		}
	}

	// NotStarted Tasks will not have a logged value
	for _, id := range sagaState.GetTaskIds() {

		taskStatus := scoot.Status_NOT_STARTED

		if sagaState.IsSagaAborted() {
			if sagaState.IsCompTaskCompleted(id) {
				taskStatus = scoot.Status_ROLLED_BACK
			} else if sagaState.IsTaskStarted(id) {
				taskStatus = scoot.Status_ROLLING_BACK
			}
		} else {
			if sagaState.IsTaskCompleted(id) {
				taskStatus = scoot.Status_COMPLETED
				if thriftJobStatus, err := workerRunStatusToScootRunStatus(sagaState.GetEndTaskData(id)); err == nil {
					js.TaskData[id] = thriftJobStatus
				}
			} else if sagaState.IsTaskStarted(id) {
				taskStatus = scoot.Status_IN_PROGRESS
				if startData := sagaState.GetStartTaskData(id); startData != nil {
					if thriftJobStatus, err := workerRunStatusToScootRunStatus(startData); err == nil {
						js.TaskData[id] = thriftJobStatus
					}
				}

			}
		}

		js.TaskStatus[id] = taskStatus
	}

	// Saga Completed Successfully
	if sagaState.IsSagaCompleted() && !sagaState.IsSagaAborted() {
		js.Status = scoot.Status_COMPLETED

		// Saga Completed Unsuccessfully was Aborted & Rolled Back
	} else if sagaState.IsSagaCompleted() && sagaState.IsSagaAborted() {
		js.Status = scoot.Status_ROLLED_BACK

		// Saga In Progress
	} else if !sagaState.IsSagaCompleted() && !sagaState.IsSagaAborted() {
		js.Status = scoot.Status_IN_PROGRESS

		// Saga in Progress - Aborted and Rolling Back
	} else if !sagaState.IsSagaCompleted() && sagaState.IsSagaAborted() {
		js.Status = scoot.Status_ROLLING_BACK
	}

	return js
}

// this is a thrift to thrift structure translation.  We are doing this because we get invalid
// import statements in the generated code when we use thrift import statements (this issue is supposed
// to be fixed in thrift 10.0
func workerRunStatusToScootRunStatus(resultsFromSaga []byte) (*scoot.RunStatus, error) {
	if resultsFromSaga == nil {
		return nil, nil
	}

	workerRunStatus := worker.RunStatus{}
	thrifthelpers.JsonDeserialize(&workerRunStatus, resultsFromSaga)

	status, err := scoot.RunStatusStateFromString(workerRunStatus.Status.String())
	if err != nil {
		return nil, err
	}

	scootRunStatus := scoot.RunStatus{
		RunId:      workerRunStatus.RunId,
		Status:     status,
		OutUri:     workerRunStatus.OutUri,
		ErrUri:     workerRunStatus.ErrUri,
		ExitCode:   workerRunStatus.ExitCode,
		Error:      workerRunStatus.Error,
		SnapshotId: workerRunStatus.SnapshotId,
	}

	return &scootRunStatus, nil
}
<|endoftext|>


<commit_before>// Packege pidctrl implements a PID controller.
//
// see http://en.wikipedia.org/wiki/PID_controller
package pidctrl

import (
	"time"
)

// NewPIDController returns a new PIDController using the given gain values.
func NewPIDController(p, i, d float64) *PIDController {
	return &PIDController{p: p, i: i, d: d}
}

// PIDController implements a PID controller.
type PIDController struct {
	p          float64   // proportional gain
	i          float64   // integral gain
	d          float64   // derrivate gain
	setpoint   float64   // current setpoint
	prevValue  float64   // last process value
	integral   float64   // integral sum
	lastUpdate time.Time // time of last update
	outMin     float64   // Output Min
	outMax     float64   // Output Max
}

// Set changes the setpoint of the controller.
func (c *PIDController) Set(setpoint float64) {
	c.setpoint = setpoint
}

// Get returns the setpoint of the controller.
func (c *PIDController) Get() float64 {
	return c.setpoint
}

// SetPID changes the P, I, and D constants
func (c *PIDController) SetPID(p, i, d float64) {
	c.p = p
	c.i = i
	c.d = d
}

// PID returns the P, I, and D constants
func (c *PIDController) PID() (p, i, d float64) {
	return c.p, c.i, c.d
}

// SetOutputLimits sets the min and max output values
func (c *PIDController) SetOutputLimits(min, max float64) {
	if min > max {
		return
	}
	c.outMin = min
	c.outMax = max

	if c.integral > c.outMax {
		c.integral = c.outMax
	} else if c.integral < c.outMin {
		c.integral = c.outMin
	}
}

// OutputLimits sets the min and max output values
func (c *PIDController) OutputLimits() (min, max float64) {
	return c.outMin, c.outMax
}

// Update is identical to UpdateDuration, but automatically keeps track of the
// durations between updates.
func (c *PIDController) Update(value float64) float64 {
	var duration time.Duration
	if !c.lastUpdate.IsZero() {
		duration = time.Since(c.lastUpdate)
	}
	c.lastUpdate = time.Now()
	return c.UpdateDuration(value, duration)
}

// UpdateDuration updates the controller with the given value and duration since
// the last update. It returns the new output.
//
// see http://en.wikipedia.org/wiki/PID_controller#Pseudocode
func (c *PIDController) UpdateDuration(value float64, duration time.Duration) float64 {
	var (
		dt  = duration.Seconds()
		err = c.setpoint - value
		d   float64
	)
	c.integral += err * dt * c.i
	if dt > 0 {
		d = -((value - c.prevValue) / dt)
	}
	c.prevValue = value
	output := (c.p * err) + c.integral + (c.d * d)

	if output > c.outMax {
		c.integral -= output - c.outMax
		output = c.outMax
	} else if output < c.outMin {
		c.integral += c.outMin - output
		output = c.outMin
	}

	return output
}
<commit_msg>Initialize min/max outputs properly<commit_after>// Packege pidctrl implements a PID controller.
//
// see http://en.wikipedia.org/wiki/PID_controller
package pidctrl

import (
	"math"
	"time"
)

// NewPIDController returns a new PIDController using the given gain values.
func NewPIDController(p, i, d float64) *PIDController {
	return &PIDController{p: p, i: i, d: d, outMin: math.Inf(-1), outMax: math.Inf(0)}
}

// PIDController implements a PID controller.
type PIDController struct {
	p          float64   // proportional gain
	i          float64   // integral gain
	d          float64   // derrivate gain
	setpoint   float64   // current setpoint
	prevValue  float64   // last process value
	integral   float64   // integral sum
	lastUpdate time.Time // time of last update
	outMin     float64   // Output Min
	outMax     float64   // Output Max
}

// Set changes the setpoint of the controller.
func (c *PIDController) Set(setpoint float64) {
	c.setpoint = setpoint
}

// Get returns the setpoint of the controller.
func (c *PIDController) Get() float64 {
	return c.setpoint
}

// SetPID changes the P, I, and D constants
func (c *PIDController) SetPID(p, i, d float64) {
	c.p = p
	c.i = i
	c.d = d
}

// PID returns the P, I, and D constants
func (c *PIDController) PID() (p, i, d float64) {
	return c.p, c.i, c.d
}

// SetOutputLimits sets the min and max output values
func (c *PIDController) SetOutputLimits(min, max float64) {
	if min > max {
		return
	}
	c.outMin = min
	c.outMax = max

	if c.integral > c.outMax {
		c.integral = c.outMax
	} else if c.integral < c.outMin {
		c.integral = c.outMin
	}
}

// OutputLimits sets the min and max output values
func (c *PIDController) OutputLimits() (min, max float64) {
	return c.outMin, c.outMax
}

// Update is identical to UpdateDuration, but automatically keeps track of the
// durations between updates.
func (c *PIDController) Update(value float64) float64 {
	var duration time.Duration
	if !c.lastUpdate.IsZero() {
		duration = time.Since(c.lastUpdate)
	}
	c.lastUpdate = time.Now()
	return c.UpdateDuration(value, duration)
}

// UpdateDuration updates the controller with the given value and duration since
// the last update. It returns the new output.
//
// see http://en.wikipedia.org/wiki/PID_controller#Pseudocode
func (c *PIDController) UpdateDuration(value float64, duration time.Duration) float64 {
	var (
		dt  = duration.Seconds()
		err = c.setpoint - value
		d   float64
	)
	c.integral += err * dt * c.i
	if dt > 0 {
		d = -((value - c.prevValue) / dt)
	}
	c.prevValue = value
	output := (c.p * err) + c.integral + (c.d * d)

	if output > c.outMax {
		c.integral -= output - c.outMax
		output = c.outMax
	} else if output < c.outMin {
		c.integral += c.outMin - output
		output = c.outMin
	}

	return output
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>

Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/

package resize

import (
	"image"
	"image/color"
	"math"
)

// restrict an input float32 to the range of uint16 values
func clampToUint16(x float32) (y uint16) {
	y = uint16(x)
	if x < 0 {
		y = 0
	} else if x > float32(0xfffe) {
		// "else if x > float32(0xffff)" will cause overflows!
		y = 0xffff
	}
	return
}

func boolToUint(b bool) (i uint) {
	if b {
		i = 0
	} else {
		i = 1
	}

	return
}

// describe a resampling filter
type filterModel struct {
	// resampling is done by convolution with a (scaled) kernel
	kernel func(float32) float32

	// instead of blurring an image before downscaling to avoid aliasing,
	// to filter is scaled by a factor which leads to a similar effect
	factor [2]float32

	// for optimized access to image points
	converter

	// temporaries used by Interpolate
	tempRow, tempCol []colorArray
}

func (f *filterModel) convolution1d(x float32, p []colorArray, isRow bool) colorArray {
	var k float32
	var sum float32 = 0
	c := colorArray{0.0, 0.0, 0.0, 0.0}

	index := boolToUint(isRow)

	for j := range p {
		k = f.kernel((x - float32(j)) / f.factor[index])
		sum += k
		for i := range c {
			c[i] += p[j][i] * k
		}
	}

	// normalize values
	for i := range c {
		c[i] = c[i] / sum
	}
	return c
}

func (f *filterModel) Interpolate(x, y float32) color.RGBA64 {
	xf, yf := int(x)-len(f.tempRow)/2+1, int(y)-len(f.tempCol)/2+1
	x -= float32(xf)
	y -= float32(yf)

	for i := 0; i < len(f.tempCol); i++ {
		for j := 0; j < len(f.tempRow); j++ {
			f.tempRow[j] = f.at(xf+j, yf+i)
		}
		f.tempCol[i] = f.convolution1d(x, f.tempRow, true)
	}

	c := f.convolution1d(y, f.tempCol, false)
	return color.RGBA64{
		clampToUint16(c[0]),
		clampToUint16(c[1]),
		clampToUint16(c[2]),
		clampToUint16(c[3]),
	}
}

// createFilter tries to find an optimized converter for the given input image
// and initializes all filterModel members to their defaults
func createFilter(img image.Image, factor [2]float32, size int, kernel func(float32) float32) (f Filter) {
	sizeX := size * (int(math.Ceil(float64(factor[0]))))
	sizeY := size * (int(math.Ceil(float64(factor[1]))))

	switch img.(type) {
	default:
		f = &filterModel{
			kernel, factor,
			&genericConverter{img},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.RGBA:
		f = &filterModel{
			kernel, factor,
			&rgbaConverter{img.(*image.RGBA)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.RGBA64:
		f = &filterModel{
			kernel, factor,
			&rgba64Converter{img.(*image.RGBA64)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.Gray:
		f = &filterModel{
			kernel, factor,
			&grayConverter{img.(*image.Gray)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.Gray16:
		f = &filterModel{
			kernel, factor,
			&gray16Converter{img.(*image.Gray16)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.YCbCr:
		f = &filterModel{
			kernel, factor,
			&ycbcrConverter{img.(*image.YCbCr)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	}
	return
}

// Nearest-neighbor interpolation
func NearestNeighbor(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 2, func(x float32) (y float32) {
		if x >= -0.5 && x < 0.5 {
			y = 1
		} else {
			y = 0
		}
		return
	})
}

// Bilinear interpolation
func Bilinear(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 2, func(x float32) (y float32) {
		absX := float32(math.Abs(float64(x)))
		if absX <= 1 {
			y = 1 - absX
		} else {
			y = 0
		}
		return
	})
}

// Bicubic interpolation (with cubic hermite spline)
func Bicubic(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 4, func(x float32) (y float32) {
		absX := float32(math.Abs(float64(x)))
		if absX <= 1 {
			y = absX*absX*(1.5*absX-2.5) + 1
		} else if absX <= 2 {
			y = absX*(absX*(2.5-0.5*absX)-4) + 2
		} else {
			y = 0
		}
		return
	})
}

// Mitchell-Netravali interpolation
func MitchellNetravali(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 4, func(x float32) (y float32) {
		absX := float32(math.Abs(float64(x)))
		if absX <= 1 {
			y = absX*absX*(7*absX-12) + 16.0/3
		} else if absX <= 2 {
			y = -(absX - 2) * (absX - 2) / 3 * (7*absX - 8)
		} else {
			y = 0
		}
		return
	})
}

func lanczosKernel(a uint) func(float32) float32 {
	return func(x float32) (y float32) {
		if x > -float32(a) && x < float32(a) {
			y = float32(Sinc(float64(x))) * float32(Sinc(float64(x/float32(a))))
		} else {
			y = 0
		}
		return
	}
}

// Lanczos interpolation (a=2)
func Lanczos2(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 4, lanczosKernel(2))
}

// Lanczos interpolation (a=3)
func Lanczos3(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 6, lanczosKernel(3))
}
<commit_msg>boolToUint: true -> 1<commit_after>/*
Copyright (c) 2012, Jan Schlicht <jan.schlicht@gmail.com>

Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/

package resize

import (
	"image"
	"image/color"
	"math"
)

// restrict an input float32 to the range of uint16 values
func clampToUint16(x float32) (y uint16) {
	y = uint16(x)
	if x < 0 {
		y = 0
	} else if x > float32(0xfffe) {
		// "else if x > float32(0xffff)" will cause overflows!
		y = 0xffff
	}
	return
}

func boolToUint(b bool) (i uint) {
	if b {
		i = 1
	} else {
		i = 0
	}

	return
}

// describe a resampling filter
type filterModel struct {
	// resampling is done by convolution with a (scaled) kernel
	kernel func(float32) float32

	// instead of blurring an image before downscaling to avoid aliasing,
	// to filter is scaled by a factor which leads to a similar effect
	factor [2]float32

	// for optimized access to image points
	converter

	// temporaries used by Interpolate
	tempRow, tempCol []colorArray
}

func (f *filterModel) convolution1d(x float32, p []colorArray, isCol bool) colorArray {
	var k float32
	var sum float32 = 0
	c := colorArray{0.0, 0.0, 0.0, 0.0}

	index := boolToUint(isCol)

	for j := range p {
		k = f.kernel((x - float32(j)) / f.factor[index])
		sum += k
		for i := range c {
			c[i] += p[j][i] * k
		}
	}

	// normalize values
	for i := range c {
		c[i] = c[i] / sum
	}
	return c
}

func (f *filterModel) Interpolate(x, y float32) color.RGBA64 {
	xf, yf := int(x)-len(f.tempRow)/2+1, int(y)-len(f.tempCol)/2+1
	x -= float32(xf)
	y -= float32(yf)

	for i := 0; i < len(f.tempCol); i++ {
		for j := 0; j < len(f.tempRow); j++ {
			f.tempRow[j] = f.at(xf+j, yf+i)
		}
		f.tempCol[i] = f.convolution1d(x, f.tempRow, false)
	}

	c := f.convolution1d(y, f.tempCol, true)
	return color.RGBA64{
		clampToUint16(c[0]),
		clampToUint16(c[1]),
		clampToUint16(c[2]),
		clampToUint16(c[3]),
	}
}

// createFilter tries to find an optimized converter for the given input image
// and initializes all filterModel members to their defaults
func createFilter(img image.Image, factor [2]float32, size int, kernel func(float32) float32) (f Filter) {
	sizeX := size * (int(math.Ceil(float64(factor[0]))))
	sizeY := size * (int(math.Ceil(float64(factor[1]))))

	switch img.(type) {
	default:
		f = &filterModel{
			kernel, factor,
			&genericConverter{img},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.RGBA:
		f = &filterModel{
			kernel, factor,
			&rgbaConverter{img.(*image.RGBA)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.RGBA64:
		f = &filterModel{
			kernel, factor,
			&rgba64Converter{img.(*image.RGBA64)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.Gray:
		f = &filterModel{
			kernel, factor,
			&grayConverter{img.(*image.Gray)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.Gray16:
		f = &filterModel{
			kernel, factor,
			&gray16Converter{img.(*image.Gray16)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	case *image.YCbCr:
		f = &filterModel{
			kernel, factor,
			&ycbcrConverter{img.(*image.YCbCr)},
			make([]colorArray, sizeX), make([]colorArray, sizeY),
		}
	}
	return
}

// Nearest-neighbor interpolation
func NearestNeighbor(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 2, func(x float32) (y float32) {
		if x >= -0.5 && x < 0.5 {
			y = 1
		} else {
			y = 0
		}
		return
	})
}

// Bilinear interpolation
func Bilinear(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 2, func(x float32) (y float32) {
		absX := float32(math.Abs(float64(x)))
		if absX <= 1 {
			y = 1 - absX
		} else {
			y = 0
		}
		return
	})
}

// Bicubic interpolation (with cubic hermite spline)
func Bicubic(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 4, func(x float32) (y float32) {
		absX := float32(math.Abs(float64(x)))
		if absX <= 1 {
			y = absX*absX*(1.5*absX-2.5) + 1
		} else if absX <= 2 {
			y = absX*(absX*(2.5-0.5*absX)-4) + 2
		} else {
			y = 0
		}
		return
	})
}

// Mitchell-Netravali interpolation
func MitchellNetravali(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 4, func(x float32) (y float32) {
		absX := float32(math.Abs(float64(x)))
		if absX <= 1 {
			y = absX*absX*(7*absX-12) + 16.0/3
		} else if absX <= 2 {
			y = -(absX - 2) * (absX - 2) / 3 * (7*absX - 8)
		} else {
			y = 0
		}
		return
	})
}

func lanczosKernel(a uint) func(float32) float32 {
	return func(x float32) (y float32) {
		if x > -float32(a) && x < float32(a) {
			y = float32(Sinc(float64(x))) * float32(Sinc(float64(x/float32(a))))
		} else {
			y = 0
		}
		return
	}
}

// Lanczos interpolation (a=2)
func Lanczos2(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 4, lanczosKernel(2))
}

// Lanczos interpolation (a=3)
func Lanczos3(img image.Image, factor [2]float32) Filter {
	return createFilter(img, factor, 6, lanczosKernel(3))
}
<|endoftext|>


<commit_before>package ogle

import (
	"errors"

	"bytes"

	"github.com/serret887/ogle/matcher"
	"golang.org/x/net/html"
)

// Find allwo to find nodes that match all the Matchers
func (o *Ogle) Find(matchers ...matcher.Matcher) ([]*html.Node, error) {
	return walkDOM(o.Node, matchers...)
}

func walkDOM(node *html.Node, matchers ...matcher.Matcher) ([]*html.Node, error) {
	result := []*html.Node{}
	if yesToProcess(node, matchers...) {
		result = append(result, node)
	}

	for n := node.FirstChild; n != nil; n = n.NextSibling {
		found, err := walkDOM(n, matchers...)
		if err == nil {
			result = append(result, found...)
		}

	}
	if len(result) < 1 {
		return nil, errorFilterCreator(matchers...)
	}
	return result, nil
}

func yesToProcess(n *html.Node, matchers ...matcher.Matcher) bool {
	for _, m := range matchers {
		if m.Match(n) == false {

			return false
		}
	}
	return true
}

func errorFilterCreator(matchers ...matcher.Matcher) error {
	var b bytes.Buffer
	b.WriteString("Problems Finding ")
	for _, m := range matchers {
		f := m.(*matcher.Filter)
		b.WriteString(f.Error())
		if len(matchers) > 1 {
			b.WriteString(" with ")
		}
	}
	return errors.New(b.String())
}
<commit_msg>Removing dependency for type filter<commit_after>package ogle

import (
	"errors"

	"bytes"

	"github.com/serret887/ogle/matcher"
	"golang.org/x/net/html"
)

// Find allwo to find nodes that match all the Matchers
func (o *Ogle) Find(matchers ...matcher.Matcher) ([]*html.Node, error) {
	return walkDOM(o.Node, matchers...)
}

func walkDOM(node *html.Node, matchers ...matcher.Matcher) ([]*html.Node, error) {
	result := []*html.Node{}
	if yesToProcess(node, matchers...) {
		result = append(result, node)
	}

	for n := node.FirstChild; n != nil; n = n.NextSibling {
		found, err := walkDOM(n, matchers...)
		if err == nil {
			result = append(result, found...)
		}

	}
	if len(result) < 1 {
		return nil, errorFilterCreator(matchers...)
	}
	return result, nil
}

func yesToProcess(n *html.Node, matchers ...matcher.Matcher) bool {
	for _, m := range matchers {
		if m.Match(n) == false {

			return false
		}
	}
	return true
}

func errorFilterCreator(matchers ...matcher.Matcher) error {
	var b bytes.Buffer
	b.WriteString("Problems Finding ")
	for _, m := range matchers {
		f := m.(error)
		b.WriteString(f.Error())
		if len(matchers) > 1 {
			b.WriteString(" with ")
		}
	}
	return errors.New(b.String())
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"log"
	"os"
)

func main() {
	app := cli.NewApp()
	app.Name = "flasher"
	app.Version = "0.1.0"
	app.Author = "Alex Jackson"
	app.Email = "alex@alexj.org"

	app.Commands = []cli.Command{
		{
			Name:      "flash",
			ShortName: "f",
			Usage:     "flasher flash [flashcard-file.json]",
			Action:    cliFlash,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "no-shuffle, n",
					Usage: "Presents flashcards in the order they are written in the source JSON file.",
				},
			},
		},
	}
	app.Run(os.Args)
}

func cliFlash(c *cli.Context) {
	if len(c.Args()) != 1 {
		log.Print("Incorrect usage")
		cli.ShowCommandHelp(c, "flash")
		os.Exit(1)
	}

	inputFlashcardStack := new(cardStack)
	err := inputFlashcardStack.loadFlashcardStack(c.Args().First())

	if err != nil {
		log.Fatal(err)
	}

	if !c.Bool("no-shuffle") {
		inputFlashcardStack.shuffle()
	}

	// Test that everything was read in correctly
	for _, card := range inputFlashcardStack.Flashcards {
		card.formatCard()
		card.showCard()
	}
}
<commit_msg>Bare bones termbox up and running.<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"github.com/nsf/termbox-go"
	"math"
	"os"
)

func main() {
	app := cli.NewApp()
	app.Name = "flasher"
	app.Version = "0.1.0"
	app.Author = "Alex Jackson"
	app.Email = "alex@alexj.org"

	app.Commands = []cli.Command{
		{
			Name:      "flash",
			ShortName: "f",
			Usage:     "flasher flash [flashcard-file.json]",
			Action:    cliFlash,
			Flags: []cli.Flag{
				cli.BoolFlag{
					Name:  "no-shuffle, n",
					Usage: "Presents flashcards in the order they are written in the source JSON file.",
				},
			},
		},
	}
	app.Run(os.Args)
}

func cliFlash(c *cli.Context) {
	// Init termbox
	err := termbox.Init()
	if err != nil {
		panic(err)
	}
	defer termbox.Close()

	draw()

	// Run loop
mainloop:
	for {
		switch event := termbox.PollEvent(); event.Type {
		case termbox.EventKey:
			switch event.Key {
			case termbox.KeyEsc:
				break mainloop
			}
		case termbox.EventResize:
			draw()
		}
		draw()
	}
}

func draw() {
	w, h := termbox.Size()
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	// Draw termbox border
	termbox.SetCell(0, 0, '+', termbox.ColorDefault, termbox.ColorDefault)
	termbox.SetCell(w-1, 0, '+', termbox.ColorDefault, termbox.ColorDefault)
	termbox.SetCell(0, h-1, '+', termbox.ColorDefault, termbox.ColorDefault)
	termbox.SetCell(w-1, h-1, '+', termbox.ColorDefault, termbox.ColorDefault)

	for x := 1; x < w-1; x++ {
		termbox.SetCell(x, 0, '-', termbox.ColorDefault, termbox.ColorDefault)
		termbox.SetCell(x, h-1, '-', termbox.ColorDefault, termbox.ColorDefault)
	}

	for y := 1; y < h-1; y++ {
		termbox.SetCell(0, y, '|', termbox.ColorDefault, termbox.ColorDefault)
		termbox.SetCell(w-1, y, '|', termbox.ColorDefault, termbox.ColorDefault)
	}

	// Draw a string in the ~centre of the termbox
	message := "Flasher will flash soon!"
	xStart := (w / 2) - int(math.Floor(float64(len(message)/2)))
	for index, runeVal := range message {
		termbox.SetCell(xStart+index, h/2, runeVal, termbox.ColorGreen, termbox.ColorDefault)
	}

	termbox.Flush()
}
<|endoftext|>