<commit_before>package console

import (
	"container/list"
	"github.com/MichaelDiBernardo/srl/lib/game"
	"github.com/MichaelDiBernardo/srl/lib/math"
	"github.com/nsf/termbox-go"
)

// The HUD screen.
type hudScreen struct {
	display      display
	mapPanel     panel
	messagePanel panel
}

// Where the map panel should go.
var mapPanelAnchor = math.Origin

// Where the message panel should go.
var messagePanelAnchor = math.Pt(1, 17)

// How many lines should show by default in the message panel.
const messagePanelNumLines = 6

// Create a new HUD.
func newHudScreen(display display) *hudScreen {
	return &hudScreen{
		display:      display,
		mapPanel:     newMapPanel(display),
		messagePanel: newMessagePanel(messagePanelNumLines, display),
	}
}

// Render the HUD.
func (h *hudScreen) Render(g *game.Game) {
	h.mapPanel.Render(g)
	h.messagePanel.Render(g)
}

// Handle an event generated by the game after the last command.
func (h *hudScreen) Handle(ev game.Event) {
	h.mapPanel.Handle(ev)
	h.messagePanel.Handle(ev)
}

// Get the next command from the player to be sent to the game instance.
func (h *hudScreen) NextCommand() game.Command {
	keymap := map[rune]game.Command{
		'h': game.CommandMoveW,
		'j': game.CommandMoveS,
		'k': game.CommandMoveN,
		'l': game.CommandMoveE,
		'q': game.CommandQuit,
	}
	for {
		tboxev := h.display.PollEvent()

		if tboxev.Type != termbox.EventKey || tboxev.Key != 0 {
			continue
		}

		srlev := keymap[tboxev.Ch]
		if srlev != 0 {
			return srlev
		}
	}
}

// Panel that renders the gameplay map.
type mapPanel struct {
	display display
}

// A glyph used to render a tile.
type glyph struct {
	Ch rune
	Fg termbox.Attribute
	Bg termbox.Attribute
}

// Glyphs used to render actors.
var actorGlyphs = map[game.ObjSubtype]glyph{
	"Player": glyph{Ch: '@', Fg: termbox.ColorWhite, Bg: termbox.ColorBlack},
	"MonOrc": glyph{Ch: 'o', Fg: termbox.ColorGreen, Bg: termbox.ColorBlack},
}

// Glyphs used to render tiles.
var featureGlyphs = map[game.FeatureType]glyph{
	"FeatWall":  glyph{Ch: '#', Fg: termbox.ColorRed, Bg: termbox.ColorBlack},
	"FeatFloor": glyph{Ch: '.', Fg: termbox.ColorWhite, Bg: termbox.ColorBlack},
}

// Create a new mapPanel.
func newMapPanel(display display) *mapPanel {
	return &mapPanel{display: display}
}

// Listens to nothing.
func (m *mapPanel) Handle(e game.Event) {
}

// Render the gameplay map to the hud.
func (m *mapPanel) Render(g *game.Game) {
	for _, row := range g.Level.Map {
		for _, tile := range row {
			pos := mapPanelAnchor.Add(tile.Pos)
			if tile.Actor != nil {
				gl := actorGlyphs[tile.Actor.Spec.Subtype]
				m.display.SetCell(pos.X, pos.Y, gl.Ch, gl.Fg, gl.Bg)
			} else {
				gl := featureGlyphs[tile.Feature.Type]
				m.display.SetCell(pos.X, pos.Y, gl.Ch, gl.Fg, gl.Bg)
			}
		}
	}
}

// The message panel, where messages are rendered at the bottom of the hud.
type messagePanel struct {
	display display
	lines   *list.List
	size    int
}

// Create a new messagePanel.
func newMessagePanel(size int, display display) *messagePanel {
	return &messagePanel{
		display: display,
		lines:   list.New(),
		size:    size,
	}
}

// Listens for new message events to build the message list.
func (m *messagePanel) Handle(e game.Event) {
	switch ev := e.(type) {
	case *game.MessageEvent:
		m.message(ev.Text)
	}
}

// Render the panel to the display.
func (m *messagePanel) Render(_ *game.Game) {
	for e, i := m.lines.Front(), 0; e != nil; e, i = e.Next(), i+1 {
		line := e.Value.(string)
		m.display.Write(messagePanelAnchor.X, messagePanelAnchor.Y+i, line, termbox.ColorWhite, termbox.ColorBlack)
	}
}

// Add a message to the list of messages to render.
func (m *messagePanel) message(text string) {
	m.lines.PushBack(text)
	if m.lines.Len() > m.size {
		m.lines.Remove(m.lines.Front())
	}
}
<commit_msg>Faking the HUD.<commit_after>package console

import (
    "fmt"
	"container/list"
	"github.com/MichaelDiBernardo/srl/lib/game"
	"github.com/MichaelDiBernardo/srl/lib/math"
	"github.com/nsf/termbox-go"
)

// The HUD screen.
type hudScreen struct {
	display      display
    panels       [3]panel
}

// Where the map panel should go.
var mapPanelAnchor = math.Origin

// Where the message panel should go.
var messagePanelAnchor = math.Pt(1, 17)

// How many lines should show by default in the message panel.
const messagePanelNumLines = 6

// Create a new HUD.
func newHudScreen(display display) *hudScreen {
	return &hudScreen{
		display:      display,
        panels: [3]panel{
            newMapPanel(display),
            newMessagePanel(messagePanelNumLines, display),
            newStatusPanel(display),
        },
	}
}

// Render the HUD.
func (h *hudScreen) Render(g *game.Game) {
    for _, p := range h.panels {
        p.Render(g)
    }
}

// Handle an event generated by the game after the last command.
func (h *hudScreen) Handle(ev game.Event) {
    for _, p := range h.panels {
        p.Handle(ev)
    }
}

// Get the next command from the player to be sent to the game instance.
func (h *hudScreen) NextCommand() game.Command {
	keymap := map[rune]game.Command{
		'h': game.CommandMoveW,
		'j': game.CommandMoveS,
		'k': game.CommandMoveN,
		'l': game.CommandMoveE,
		'q': game.CommandQuit,
	}
	for {
		tboxev := h.display.PollEvent()

		if tboxev.Type != termbox.EventKey || tboxev.Key != 0 {
			continue
		}

		srlev := keymap[tboxev.Ch]
		if srlev != 0 {
			return srlev
		}
	}
}

// Panel that renders the gameplay map.
type mapPanel struct {
	display display
}

// A glyph used to render a tile.
type glyph struct {
	Ch rune
	Fg termbox.Attribute
	Bg termbox.Attribute
}

// Glyphs used to render actors.
var actorGlyphs = map[game.ObjSubtype]glyph{
	"Player": glyph{Ch: '@', Fg: termbox.ColorWhite, Bg: termbox.ColorBlack},
	"MonOrc": glyph{Ch: 'o', Fg: termbox.ColorGreen, Bg: termbox.ColorBlack},
}

// Glyphs used to render tiles.
var featureGlyphs = map[game.FeatureType]glyph{
	"FeatWall":  glyph{Ch: '#', Fg: termbox.ColorRed, Bg: termbox.ColorBlack},
	"FeatFloor": glyph{Ch: '.', Fg: termbox.ColorWhite, Bg: termbox.ColorBlack},
}

// Create a new mapPanel.
func newMapPanel(display display) *mapPanel {
	return &mapPanel{display: display}
}

// Listens to nothing.
func (m *mapPanel) Handle(e game.Event) {
}

// Render the gameplay map to the hud.
func (m *mapPanel) Render(g *game.Game) {
	for _, row := range g.Level.Map {
		for _, tile := range row {
			pos := mapPanelAnchor.Add(tile.Pos)
			if tile.Actor != nil {
				gl := actorGlyphs[tile.Actor.Spec.Subtype]
				m.display.SetCell(pos.X, pos.Y, gl.Ch, gl.Fg, gl.Bg)
			} else {
				gl := featureGlyphs[tile.Feature.Type]
				m.display.SetCell(pos.X, pos.Y, gl.Ch, gl.Fg, gl.Bg)
			}
		}
	}
}

// The message panel, where messages are rendered at the bottom of the hud.
type messagePanel struct {
	display display
	lines   *list.List
	size    int
}

// Create a new messagePanel.
func newMessagePanel(size int, display display) *messagePanel {
	return &messagePanel{
		display: display,
		lines:   list.New(),
		size:    size,
	}
}

// Listens for new message events to build the message list.
func (m *messagePanel) Handle(e game.Event) {
	switch ev := e.(type) {
	case *game.MessageEvent:
		m.message(ev.Text)
	}
}

// Render the panel to the display.
func (m *messagePanel) Render(_ *game.Game) {
	for e, i := m.lines.Front(), 0; e != nil; e, i = e.Next(), i+1 {
		line := e.Value.(string)
		m.display.Write(messagePanelAnchor.X, messagePanelAnchor.Y+i, line, termbox.ColorWhite, termbox.ColorBlack)
	}
}

// Add a message to the list of messages to render.
func (m *messagePanel) message(text string) {
	m.lines.PushBack(text)
	if m.lines.Len() > m.size {
		m.lines.Remove(m.lines.Front())
	}
}

// Panel that renders player status on hud.
type statusPanel struct {
	display display
}

var statusPanelAnchor = math.Pt(38, 0)

// Create a new statsPanel.
func newStatusPanel(display display) *statusPanel {
	return &statusPanel{display: display}
}

// Listens for nothing.
func (s *statusPanel) Handle(e game.Event) {
}

// Render the panel to the display.
func (s *statusPanel) Render(g *game.Game) {
    player := g.Player
    fg, bg := termbox.ColorWhite, termbox.ColorBlack

    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 0, player.Spec.Name, fg, bg)
    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 1, "Human", fg, bg)

    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 3, fmt.Sprintf("%-7s%3d", "STR", 1), fg, bg)
    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 4, fmt.Sprintf("%-7s%3d", "AGI", 2), fg, bg)
    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 5, fmt.Sprintf("%-7s%3d", "VIT", 2), fg, bg)
    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 6, fmt.Sprintf("%-7s%3d", "MND", 0), fg, bg)

    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 8, fmt.Sprintf("%-7s%3d", "HP", 30), fg, bg)
    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 9, fmt.Sprintf("%-7s%3d", "MP", 10), fg, bg)

    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 11, fmt.Sprintf("%-7s%8s", "FIGHT", "(+2,2d6)"), fg, bg)
    s.display.Write(statusPanelAnchor.X, statusPanelAnchor.Y + 12, fmt.Sprintf("%-7s%8s", "DEF", "[+0,1-3]"), fg, bg)
}
<|endoftext|>


<commit_before>package main

import (
	//"fmt"
	"net/http"
	"os"
	"strconv"
	"sync"

	"github.com/PuerkitoBio/goquery"
)

const (
	BASE_URL = "https://projecteuler.net/problem="
)

var wg sync.WaitGroup

func main() {
	for i := 1; i < 523; i++ {
		wg.Add(1)
		go fetch(i)
	}
	wg.Wait()
}

func fetch(i int) {
	num := strconv.Itoa(i)
	url := BASE_URL + num
	defer wg.Done()
	resp, err := http.Get(url)

	if err != nil || resp.StatusCode != http.StatusOK {
		panic(err)
	} else {
		defer resp.Body.Close()
		doc, err := goquery.NewDocumentFromReader(resp.Body)
		if err != nil {
			panic(err)
		} else {
			var title string = ""
			var problemContent string = ""
			doc.Find("#content h2").Each(func(i int, s *goquery.Selection) {
				title = s.Text()
			})

			doc.Find("#content .problem_content").Each(func(i int, s *goquery.Selection) {
				problemContent = s.Text()
			})

			//write to file
			path, err := os.Getwd()
			if err != nil {
				panic(err)
			}

			pathSeparator := string(byte(os.PathSeparator))
			filepath := path + pathSeparator + pathSeparator + "prob" + num + ".go"

			f, err := os.Create(filepath)
			if err != nil {
				panic(err)
			} else {
				println(url)
			}
			defer f.Close()

			_, _ = f.WriteString("package main \n\n\n/**\n" + url + "\n\n" + title + "\n" + problemContent + "**/\n")
			f.Sync()
		}
	}
}
<commit_msg>better things<commit_after>package main

import (
	//"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"sync"

	"github.com/PuerkitoBio/goquery"
)

const (
	BASE_URL = "https://projecteuler.net/problem="
)

var wg sync.WaitGroup

func main() {
	for i := 1; i < 523; i++ {
		wg.Add(1)
		go fetch(i)
	}
	wg.Wait()
}

func fetch(i int) {
	num := strconv.Itoa(i)
	url := BASE_URL + num
	defer wg.Done()
	resp, err := http.Get(url)

	if err != nil {
		log.Fatal(err)
	} else {
		defer resp.Body.Close()
		doc, err := goquery.NewDocumentFromReader(resp.Body)
		if err != nil {
			log.Fatal(err)
		} else {
			var title string = ""
			var problemContent string = ""
			doc.Find("#content h2").Each(func(i int, s *goquery.Selection) {
				title = s.Text()
			})

			doc.Find("#content .problem_content").Each(func(i int, s *goquery.Selection) {
				problemContent = s.Text()
			})

			//write to file
			path, err := os.Getwd()
			if err != nil {
				log.Fatal(err)
			}

			pathSeparator := string(byte(os.PathSeparator))
			filepath := path + pathSeparator + pathSeparator + "prob" + num + ".go"

			f, err := os.Create(filepath)
			if err != nil {
				log.Fatal(err)
			}

			defer f.Close()

			f.WriteString("package main \n\n\n/**\n" + url + "\n\n" + title + "\n" + problemContent + "**/\n")
			f.Sync()
		}
	}
}
<|endoftext|>


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

// Package spinner is a simple package to add a spinner / progress indicator to any terminal application.
package spinner

import (
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/fatih/color"
)

// errInvalidColor is returned when attempting to set an invalid color
var errInvalidColor = errors.New("invalid color")

// validColors holds an array of the only colors allowed
var validColors = map[string]bool{
	// default colors for backwards compatibility
	"black":   true,
	"red":     true,
	"green":   true,
	"yellow":  true,
	"blue":    true,
	"magenta": true,
	"cyan":    true,
	"white":   true,

	// attributes
	"reset":        true,
	"bold":         true,
	"faint":        true,
	"italic":       true,
	"underline":    true,
	"blinkslow":    true,
	"blinkrapid":   true,
	"reversevideo": true,
	"concealed":    true,
	"crossedout":   true,

	// foreground text
	"fgBlack":   true,
	"fgRed":     true,
	"fgGreen":   true,
	"fgYellow":  true,
	"fgBlue":    true,
	"fgMagenta": true,
	"fgCyan":    true,
	"fgWhite":   true,

	// foreground Hi-Intensity text
	"fgHiBlack":   true,
	"fgHiRed":     true,
	"fgHiGreen":   true,
	"fgHiYellow":  true,
	"fgHiBlue":    true,
	"fgHiMagenta": true,
	"fgHiCyan":    true,
	"fgHiWhite":   true,

	// background text
	"bgBlack":   true,
	"bgRed":     true,
	"bgGreen":   true,
	"bgYellow":  true,
	"bgBlue":    true,
	"bgMagenta": true,
	"bgCyan":    true,
	"bgWhite":   true,

	// background Hi-Intensity text
	"bgHiBlack":   true,
	"bgHiRed":     true,
	"bgHiGreen":   true,
	"bgHiYellow":  true,
	"bgHiBlue":    true,
	"bgHiMagenta": true,
	"bgHiCyan":    true,
	"bgHiWhite":   true,
}

// returns a valid color's foreground text color attribute
var colorAttributeMap = map[string]color.Attribute{
	// default colors for backwards compatibility
	"black":   color.FgBlack,
	"red":     color.FgRed,
	"green":   color.FgGreen,
	"yellow":  color.FgYellow,
	"blue":    color.FgBlue,
	"magenta": color.FgMagenta,
	"cyan":    color.FgCyan,
	"white":   color.FgWhite,

	// attributes
	"reset":        color.Reset,
	"bold":         color.Bold,
	"faint":        color.Faint,
	"italic":       color.Italic,
	"underline":    color.Underline,
	"blinkslow":    color.BlinkSlow,
	"blinkrapid":   color.BlinkRapid,
	"reversevideo": color.ReverseVideo,
	"concealed":    color.Concealed,
	"crossedout":   color.CrossedOut,

	// foreground text colors
	"fgBlack":   color.FgBlack,
	"fgRed":     color.FgRed,
	"fgGreen":   color.FgGreen,
	"fgYellow":  color.FgYellow,
	"fgBlue":    color.FgBlue,
	"fgMagenta": color.FgMagenta,
	"fgCyan":    color.FgCyan,
	"fgWhite":   color.FgWhite,

	// foreground Hi-Intensity text colors
	"fgHiBlack":   color.FgHiBlack,
	"fgHiRed":     color.FgHiRed,
	"fgHiGreen":   color.FgHiGreen,
	"fgHiYellow":  color.FgHiYellow,
	"fgHiBlue":    color.FgHiBlue,
	"fgHiMagenta": color.FgHiMagenta,
	"fgHiCyan":    color.FgHiCyan,
	"fgHiWhite":   color.FgHiWhite,

	// background text colors
	"bgBlack":   color.BgBlack,
	"bgRed":     color.BgRed,
	"bgGreen":   color.BgGreen,
	"bgYellow":  color.BgYellow,
	"bgBlue":    color.BgBlue,
	"bgMagenta": color.BgMagenta,
	"bgCyan":    color.BgCyan,
	"bgWhite":   color.BgWhite,

	// background Hi-Intensity text colors
	"bgHiBlack":   color.BgHiBlack,
	"bgHiRed":     color.BgHiRed,
	"bgHiGreen":   color.BgHiGreen,
	"bgHiYellow":  color.BgHiYellow,
	"bgHiBlue":    color.BgHiBlue,
	"bgHiMagenta": color.BgHiMagenta,
	"bgHiCyan":    color.BgHiCyan,
	"bgHiWhite":   color.BgHiWhite,
}

// validColor will make sure the given color is actually allowed
func validColor(c string) bool {
	valid := false
	if validColors[c] {
		valid = true
	}
	return valid
}

// Spinner struct to hold the provided options
type Spinner struct {
	Delay      time.Duration                 // Delay is the speed of the indicator
	chars      []string                      // chars holds the chosen character set
	Prefix     string                        // Prefix is the text preppended to the indicator
	Suffix     string                        // Suffix is the text appended to the indicator
	FinalMSG   string                        // string displayed after Stop() is called
	lastOutput string                        // last character(set) written
	color      func(a ...interface{}) string // default color is white
	lock       *sync.RWMutex                 //
	Writer     io.Writer                     // to make testing better, exported so users have access
	active     bool                          // active holds the state of the spinner
	stopChan   chan struct{}                 // stopChan is a channel used to stop the indicator
	HideCursor bool                          // hideCursor determines if the cursor is visible
}

// New provides a pointer to an instance of Spinner with the supplied options
func New(cs []string, d time.Duration, options ...Option) *Spinner {
	s := &Spinner{
		Delay:    d,
		chars:    cs,
		color:    color.New(color.FgWhite).SprintFunc(),
		lock:     &sync.RWMutex{},
		Writer:   color.Output,
		active:   false,
		stopChan: make(chan struct{}, 1),
	}

	for _, option := range options {
		option(s)
	}
	return s
}

// Option is a function that takes a spinner and applies
// a given configuration
type Option func(*Spinner)

// Options contains fields to configure the spinner
type Options struct {
	Color      string
	Suffix     string
	FinalMSG   string
	HideCursor bool
}

// WithColor adds the given color to the spinner
func WithColor(color string) Option {
	return func(s *Spinner) {
		s.Color(color)
	}
}

// WithSuffix adds the given string to the spinner
// as the suffix
func WithSuffix(suffix string) Option {
	return func(s *Spinner) {
		s.Suffix = suffix
	}
}

// WithFinalMSG adds the given string ot the spinner
// as the final message to be written
func WithFinalMSG(finalMsg string) Option {
	return func(s *Spinner) {
		s.FinalMSG = finalMsg
	}
}

// WithHiddenCursor hides the cursor
// if hideCursor = true given
func WithHiddenCursor(hideCursor bool) Option {
	return func(s *Spinner) {
		s.HideCursor = hideCursor
	}
}

// Active will return whether or not the spinner is currently active
func (s *Spinner) Active() bool {
	return s.active
}

// Start will start the indicator
func (s *Spinner) Start() {
	s.lock.Lock()
	if s.active {
		s.lock.Unlock()
		return
	}
	if s.HideCursor && runtime.GOOS != "windows" {
		// hides the cursor
		fmt.Print("\033[?25l")
	}
	s.active = true
	s.lock.Unlock()

	go func() {
		for {
			for i := 0; i < len(s.chars); i++ {
				select {
				case <-s.stopChan:
					return
				default:
					s.lock.Lock()
					s.erase()
					var outColor string
					if runtime.GOOS == "windows" {
						if s.Writer == os.Stderr {
							outColor = fmt.Sprintf("\r%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
						} else {
							outColor = fmt.Sprintf("\r%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
						}
					} else {
						outColor = fmt.Sprintf("%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
					}
					outPlain := fmt.Sprintf("%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
					fmt.Fprint(s.Writer, outColor)
					s.lastOutput = outPlain
					delay := s.Delay
					s.lock.Unlock()

					time.Sleep(delay)
				}
			}
		}
	}()
}

// Stop stops the indicator
func (s *Spinner) Stop() {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.active {
		s.active = false
		if s.HideCursor && runtime.GOOS != "windows" {
			// makes the cursor visible
			fmt.Print("\033[?25h")
		}
		s.erase()
		if s.FinalMSG != "" {
			fmt.Fprintf(s.Writer, s.FinalMSG)
		}
		s.stopChan <- struct{}{}
	}
}

// Restart will stop and start the indicator
func (s *Spinner) Restart() {
	s.Stop()
	s.Start()
}

// Reverse will reverse the order of the slice assigned to the indicator
func (s *Spinner) Reverse() {
	s.lock.Lock()
	defer s.lock.Unlock()
	for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
		s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
	}
}

// Color will set the struct field for the given color to be used
func (s *Spinner) Color(colors ...string) error {

	colorAttributes := make([]color.Attribute, len(colors))

	// Verify colours are valid and place the appropriate attribute in the array
	for index, c := range colors {
		if !validColor(c) {
			return errInvalidColor
		}

		colorAttributes[index] = colorAttributeMap[c]
	}

	s.lock.Lock()
	s.color = color.New(colorAttributes...).SprintFunc()
	s.lock.Unlock()
	s.Restart()
	return nil
}

// UpdateSpeed will set the indicator delay to the given value
func (s *Spinner) UpdateSpeed(d time.Duration) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.Delay = d
}

// UpdateCharSet will change the current character set to the given one
func (s *Spinner) UpdateCharSet(cs []string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.chars = cs
}

// erase deletes written characters
//
// Caller must already hold s.lock.
func (s *Spinner) erase() {
	n := utf8.RuneCountInString(s.lastOutput)
	if runtime.GOOS == "windows" {
		clearString := "\r"
		for i := 0; i < n; i++ {
			clearString += " "
		}
		fmt.Fprintf(s.Writer, clearString)
		return
	}
	del, _ := hex.DecodeString("7f")
	for _, c := range []string{
		"\b",
		string(del),
		"\b",
		"\033[K", // for macOS Terminal
	} {
		for i := 0; i < n; i++ {
			fmt.Fprintf(s.Writer, c)
		}
	}
	s.lastOutput = ""
}

// Lock allows for manual control to lock the spinner
func (s *Spinner) Lock() {
	s.lock.Lock()
}

// Unlock allows for manual control to unlock the spinner
func (s *Spinner) Unlock() {
	s.lock.Unlock()
}

// GenerateNumberSequence will generate a slice of integers at the
// provided length and convert them each to a string
func GenerateNumberSequence(length int) []string {
	numSeq := make([]string, length)
	for i := 0; i < length; i++ {
		numSeq[i] = strconv.Itoa(i)
	}
	return numSeq
}
<commit_msg>Fix for erroneous whitespace after stopping a spinner on windows<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package spinner is a simple package to add a spinner / progress indicator to any terminal application.
package spinner

import (
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"os"
	"runtime"
	"strconv"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/fatih/color"
)

// errInvalidColor is returned when attempting to set an invalid color
var errInvalidColor = errors.New("invalid color")

// validColors holds an array of the only colors allowed
var validColors = map[string]bool{
	// default colors for backwards compatibility
	"black":   true,
	"red":     true,
	"green":   true,
	"yellow":  true,
	"blue":    true,
	"magenta": true,
	"cyan":    true,
	"white":   true,

	// attributes
	"reset":        true,
	"bold":         true,
	"faint":        true,
	"italic":       true,
	"underline":    true,
	"blinkslow":    true,
	"blinkrapid":   true,
	"reversevideo": true,
	"concealed":    true,
	"crossedout":   true,

	// foreground text
	"fgBlack":   true,
	"fgRed":     true,
	"fgGreen":   true,
	"fgYellow":  true,
	"fgBlue":    true,
	"fgMagenta": true,
	"fgCyan":    true,
	"fgWhite":   true,

	// foreground Hi-Intensity text
	"fgHiBlack":   true,
	"fgHiRed":     true,
	"fgHiGreen":   true,
	"fgHiYellow":  true,
	"fgHiBlue":    true,
	"fgHiMagenta": true,
	"fgHiCyan":    true,
	"fgHiWhite":   true,

	// background text
	"bgBlack":   true,
	"bgRed":     true,
	"bgGreen":   true,
	"bgYellow":  true,
	"bgBlue":    true,
	"bgMagenta": true,
	"bgCyan":    true,
	"bgWhite":   true,

	// background Hi-Intensity text
	"bgHiBlack":   true,
	"bgHiRed":     true,
	"bgHiGreen":   true,
	"bgHiYellow":  true,
	"bgHiBlue":    true,
	"bgHiMagenta": true,
	"bgHiCyan":    true,
	"bgHiWhite":   true,
}

// returns a valid color's foreground text color attribute
var colorAttributeMap = map[string]color.Attribute{
	// default colors for backwards compatibility
	"black":   color.FgBlack,
	"red":     color.FgRed,
	"green":   color.FgGreen,
	"yellow":  color.FgYellow,
	"blue":    color.FgBlue,
	"magenta": color.FgMagenta,
	"cyan":    color.FgCyan,
	"white":   color.FgWhite,

	// attributes
	"reset":        color.Reset,
	"bold":         color.Bold,
	"faint":        color.Faint,
	"italic":       color.Italic,
	"underline":    color.Underline,
	"blinkslow":    color.BlinkSlow,
	"blinkrapid":   color.BlinkRapid,
	"reversevideo": color.ReverseVideo,
	"concealed":    color.Concealed,
	"crossedout":   color.CrossedOut,

	// foreground text colors
	"fgBlack":   color.FgBlack,
	"fgRed":     color.FgRed,
	"fgGreen":   color.FgGreen,
	"fgYellow":  color.FgYellow,
	"fgBlue":    color.FgBlue,
	"fgMagenta": color.FgMagenta,
	"fgCyan":    color.FgCyan,
	"fgWhite":   color.FgWhite,

	// foreground Hi-Intensity text colors
	"fgHiBlack":   color.FgHiBlack,
	"fgHiRed":     color.FgHiRed,
	"fgHiGreen":   color.FgHiGreen,
	"fgHiYellow":  color.FgHiYellow,
	"fgHiBlue":    color.FgHiBlue,
	"fgHiMagenta": color.FgHiMagenta,
	"fgHiCyan":    color.FgHiCyan,
	"fgHiWhite":   color.FgHiWhite,

	// background text colors
	"bgBlack":   color.BgBlack,
	"bgRed":     color.BgRed,
	"bgGreen":   color.BgGreen,
	"bgYellow":  color.BgYellow,
	"bgBlue":    color.BgBlue,
	"bgMagenta": color.BgMagenta,
	"bgCyan":    color.BgCyan,
	"bgWhite":   color.BgWhite,

	// background Hi-Intensity text colors
	"bgHiBlack":   color.BgHiBlack,
	"bgHiRed":     color.BgHiRed,
	"bgHiGreen":   color.BgHiGreen,
	"bgHiYellow":  color.BgHiYellow,
	"bgHiBlue":    color.BgHiBlue,
	"bgHiMagenta": color.BgHiMagenta,
	"bgHiCyan":    color.BgHiCyan,
	"bgHiWhite":   color.BgHiWhite,
}

// validColor will make sure the given color is actually allowed
func validColor(c string) bool {
	valid := false
	if validColors[c] {
		valid = true
	}
	return valid
}

// Spinner struct to hold the provided options
type Spinner struct {
	Delay      time.Duration                 // Delay is the speed of the indicator
	chars      []string                      // chars holds the chosen character set
	Prefix     string                        // Prefix is the text preppended to the indicator
	Suffix     string                        // Suffix is the text appended to the indicator
	FinalMSG   string                        // string displayed after Stop() is called
	lastOutput string                        // last character(set) written
	color      func(a ...interface{}) string // default color is white
	lock       *sync.RWMutex                 //
	Writer     io.Writer                     // to make testing better, exported so users have access
	active     bool                          // active holds the state of the spinner
	stopChan   chan struct{}                 // stopChan is a channel used to stop the indicator
	HideCursor bool                          // hideCursor determines if the cursor is visible
}

// New provides a pointer to an instance of Spinner with the supplied options
func New(cs []string, d time.Duration, options ...Option) *Spinner {
	s := &Spinner{
		Delay:    d,
		chars:    cs,
		color:    color.New(color.FgWhite).SprintFunc(),
		lock:     &sync.RWMutex{},
		Writer:   color.Output,
		active:   false,
		stopChan: make(chan struct{}, 1),
	}

	for _, option := range options {
		option(s)
	}
	return s
}

// Option is a function that takes a spinner and applies
// a given configuration
type Option func(*Spinner)

// Options contains fields to configure the spinner
type Options struct {
	Color      string
	Suffix     string
	FinalMSG   string
	HideCursor bool
}

// WithColor adds the given color to the spinner
func WithColor(color string) Option {
	return func(s *Spinner) {
		s.Color(color)
	}
}

// WithSuffix adds the given string to the spinner
// as the suffix
func WithSuffix(suffix string) Option {
	return func(s *Spinner) {
		s.Suffix = suffix
	}
}

// WithFinalMSG adds the given string ot the spinner
// as the final message to be written
func WithFinalMSG(finalMsg string) Option {
	return func(s *Spinner) {
		s.FinalMSG = finalMsg
	}
}

// WithHiddenCursor hides the cursor
// if hideCursor = true given
func WithHiddenCursor(hideCursor bool) Option {
	return func(s *Spinner) {
		s.HideCursor = hideCursor
	}
}

// Active will return whether or not the spinner is currently active
func (s *Spinner) Active() bool {
	return s.active
}

// Start will start the indicator
func (s *Spinner) Start() {
	s.lock.Lock()
	if s.active {
		s.lock.Unlock()
		return
	}
	if s.HideCursor && runtime.GOOS != "windows" {
		// hides the cursor
		fmt.Print("\033[?25l")
	}
	s.active = true
	s.lock.Unlock()

	go func() {
		for {
			for i := 0; i < len(s.chars); i++ {
				select {
				case <-s.stopChan:
					return
				default:
					s.lock.Lock()
					s.erase()
					var outColor string
					if runtime.GOOS == "windows" {
						if s.Writer == os.Stderr {
							outColor = fmt.Sprintf("\r%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
						} else {
							outColor = fmt.Sprintf("\r%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
						}
					} else {
						outColor = fmt.Sprintf("%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
					}
					outPlain := fmt.Sprintf("%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
					fmt.Fprint(s.Writer, outColor)
					s.lastOutput = outPlain
					delay := s.Delay
					s.lock.Unlock()

					time.Sleep(delay)
				}
			}
		}
	}()
}

// Stop stops the indicator
func (s *Spinner) Stop() {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.active {
		s.active = false
		if s.HideCursor && runtime.GOOS != "windows" {
			// makes the cursor visible
			fmt.Print("\033[?25h")
		}
		s.erase()
		if s.FinalMSG != "" {
			fmt.Fprintf(s.Writer, s.FinalMSG)
		}
		s.stopChan <- struct{}{}
	}
}

// Restart will stop and start the indicator
func (s *Spinner) Restart() {
	s.Stop()
	s.Start()
}

// Reverse will reverse the order of the slice assigned to the indicator
func (s *Spinner) Reverse() {
	s.lock.Lock()
	defer s.lock.Unlock()
	for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
		s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
	}
}

// Color will set the struct field for the given color to be used
func (s *Spinner) Color(colors ...string) error {

	colorAttributes := make([]color.Attribute, len(colors))

	// Verify colours are valid and place the appropriate attribute in the array
	for index, c := range colors {
		if !validColor(c) {
			return errInvalidColor
		}

		colorAttributes[index] = colorAttributeMap[c]
	}

	s.lock.Lock()
	s.color = color.New(colorAttributes...).SprintFunc()
	s.lock.Unlock()
	s.Restart()
	return nil
}

// UpdateSpeed will set the indicator delay to the given value
func (s *Spinner) UpdateSpeed(d time.Duration) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.Delay = d
}

// UpdateCharSet will change the current character set to the given one
func (s *Spinner) UpdateCharSet(cs []string) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.chars = cs
}

// erase deletes written characters
//
// Caller must already hold s.lock.
func (s *Spinner) erase() {
	n := utf8.RuneCountInString(s.lastOutput)
	if runtime.GOOS == "windows" {
		clearString := ""
		for i := 0; i < n; i++ {
			clearString += " "
		}
		clearString += "\r"
		fmt.Fprintf(s.Writer, clearString)
		s.lastOutput = ""
		return
	}
	del, _ := hex.DecodeString("7f")
	for _, c := range []string{
		"\b",
		string(del),
		"\b",
		"\033[K", // for macOS Terminal
	} {
		for i := 0; i < n; i++ {
			fmt.Fprintf(s.Writer, c)
		}
	}
	s.lastOutput = ""
}

// Lock allows for manual control to lock the spinner
func (s *Spinner) Lock() {
	s.lock.Lock()
}

// Unlock allows for manual control to unlock the spinner
func (s *Spinner) Unlock() {
	s.lock.Unlock()
}

// GenerateNumberSequence will generate a slice of integers at the
// provided length and convert them each to a string
func GenerateNumberSequence(length int) []string {
	numSeq := make([]string, length)
	for i := 0; i < length; i++ {
		numSeq[i] = strconv.Itoa(i)
	}
	return numSeq
}
<|endoftext|>


<commit_before>package main

import (
  "strconv"
  "net/http"
  "log"
  "io/ioutil"
  "sync"
  "os"
)

const PACKETLENGTH = 32000
var wg sync.WaitGroup

func downloadPacket(client *http.Client, req *http.Request,part_filename string,byteStart, byteEnd int){
    resp, err := client.Do(req)
    if err != nil {
      log.Fatal(err)
    }
    reader, err := ioutil.ReadAll(resp.Body)
    if err != nil {
      log.Fatal(err)
    }
    log.Println(part_filename, len(reader))
    err = writeBytes(part_filename,reader,byteStart,byteEnd)
    if err != nil {
      log.Fatal(err)
    }
}

func downloadPart(url,filename string, index, byteStart, byteEnd int){
    client := &http.Client{}
    part_filename := filename + "_" + strconv.Itoa(index)
    noofpacket := (byteEnd - byteStart + 1)/PACKETLENGTH + 1
    for i := 0 ; i < noofpacket; i ++ {
      packetStart := byteStart + i*PACKETLENGTH
      packetEnd   := packetStart + PACKETLENGTH
      if (i == noofpacket - 1){
        packetEnd = byteEnd
      }
      range_header := "bytes=" + strconv.Itoa(packetStart) +"-" + strconv.Itoa(packetEnd-1)
      //log.Println(range_header)
      req, _ := http.NewRequest("GET",url, nil)
      req.Header.Add("Range", range_header)
      downloadPacket(client,req,part_filename,byteStart,byteEnd)
    }
    wg.Done()
}

func Download(url string,length int){
    partLength := length / *noOfFiles
    filename := getFilenameFromUrl(url)
    for i := 0 ; i < *noOfFiles ; i++ {
      byteStart := partLength * (i)
      byteEnd   := byteStart + partLength
      if (i == *noOfFiles - 1 ){
        byteEnd = length
      }
      os.MkdirAll("temp/", 0777)
      createTempFile("temp/" + filename + "_" + strconv.Itoa(i),byteStart,byteEnd)
      wg.Add(1)
      go downloadPart(url,filename,i,byteStart,byteEnd)
    }
    wg.Wait()
    mergeFiles(filename,*noOfFiles)
    clearFiles(filename,*noOfFiles)
    reader,_ := ioutil.ReadFile(filename)
    log.Println(len(reader))
}

func Resume(url string,length int){
    filename := getFilenameFromUrl(url)
    *noOfFiles = noOfExistingConnection(filename,length)
    partLength := length / *noOfFiles
    for i := 0 ; i < *noOfFiles ; i++ {
      part_filename := "temp/" +filename + "_" + strconv.Itoa(i)
      if _, err := os.Stat(part_filename); err != nil {
        byteStart := partLength * (i)
        byteEnd   := byteStart + partLength
        if (i == *noOfFiles - 1 ){
          byteEnd = length
        }
        wg.Add(1)
        go downloadPart(url,filename,i,byteStart,byteEnd)
      } else {
        byteStart, byteEnd := readHeader(part_filename)
        if (byteStart < byteEnd) {
          wg.Add(1)
          go downloadPart(url,filename,i,byteStart,byteEnd)
        }
      }
    }
    wg.Wait()
    mergeFiles(filename,*noOfFiles)
    clearFiles(filename,*noOfFiles)
    reader,_ := ioutil.ReadFile(filename)
    log.Println(len(reader))
}

func DownloadSingle(url string){
    filename := getFilenameFromUrl(url)
    client := &http.Client{}
    req, _ := http.NewRequest("GET",url, nil)
    resp, err := client.Do(req)
    if err != nil {
      log.Fatal(err)
    }
    reader, err := ioutil.ReadAll(resp.Body)
    if err != nil {
      log.Fatal(err)
    }
    log.Println(len(reader))
    err = ioutil.WriteFile(filename, reader,0666)
    if err != nil {
      log.Fatal(err)
    }
}
<commit_msg>connection timeout added<commit_after>package main

import (
  "strconv"
  "net/http"
  "log"
  "io/ioutil"
  "sync"
  "os"
  "time"
  "errors"
)

type http_response struct {
    resp *http.Response
    err  error
}

const PACKETLENGTH = 32000
var wg sync.WaitGroup

func downloadPacket(client *http.Client, req *http.Request,part_filename string,byteStart, byteEnd int) error {
    c := make(chan http_response, 1)
    go func() {
      resp,err := client.Do(req)
      http_response := http_response{resp,err}
      c <- http_response
    }()
    select {
    case http_response := <-c:
      if http_response.err != nil{
        return http_response.err
      }
      defer http_response.resp.Body.Close()
      reader, err := ioutil.ReadAll(http_response.resp.Body)
      if err != nil {
        return err
      }
      log.Println(part_filename, len(reader))
      err = writeBytes(part_filename,reader,byteStart,byteEnd)
      if err != nil {
        return err
      }
    case <-time.After(time.Second * time.Duration(10)):
      err := errors.New("Manual time out as response not recieved")
      return err
    }
    return nil
}

func downloadPart(url,filename string, index, byteStart, byteEnd int){
    client := &http.Client{}
    part_filename := filename + "_" + strconv.Itoa(index)
    noofpacket := (byteEnd - byteStart + 1)/PACKETLENGTH + 1
    for i := 0 ; i < noofpacket; i ++ {
      packetStart := byteStart + i*PACKETLENGTH
      packetEnd   := packetStart + PACKETLENGTH
      if (i == noofpacket - 1){
        packetEnd = byteEnd
      }
      range_header := "bytes=" + strconv.Itoa(packetStart) +"-" + strconv.Itoa(packetEnd-1)
      //log.Println(range_header)
      req, _ := http.NewRequest("GET",url, nil)
      req.Header.Add("Range", range_header)
      downloadPacket(client,req,part_filename,byteStart,byteEnd)
    }
    wg.Done()
}

func Download(url string,length int){
    partLength := length / *noOfFiles
    filename := getFilenameFromUrl(url)
    for i := 0 ; i < *noOfFiles ; i++ {
      byteStart := partLength * (i)
      byteEnd   := byteStart + partLength
      if (i == *noOfFiles - 1 ){
        byteEnd = length
      }
      os.MkdirAll("temp/", 0777)
      createTempFile("temp/" + filename + "_" + strconv.Itoa(i),byteStart,byteEnd)
      wg.Add(1)
      go downloadPart(url,filename,i,byteStart,byteEnd)
    }
    wg.Wait()
    mergeFiles(filename,*noOfFiles)
    clearFiles(filename,*noOfFiles)
    reader,_ := ioutil.ReadFile(filename)
    log.Println(len(reader))
}

func Resume(url string,length int){
    filename := getFilenameFromUrl(url)
    *noOfFiles = noOfExistingConnection(filename,length)
    partLength := length / *noOfFiles
    for i := 0 ; i < *noOfFiles ; i++ {
      part_filename := "temp/" +filename + "_" + strconv.Itoa(i)
      if _, err := os.Stat(part_filename); err != nil {
        byteStart := partLength * (i)
        byteEnd   := byteStart + partLength
        if (i == *noOfFiles - 1 ){
          byteEnd = length
        }
        wg.Add(1)
        go downloadPart(url,filename,i,byteStart,byteEnd)
      } else {
        byteStart, byteEnd := readHeader(part_filename)
        if (byteStart < byteEnd) {
          wg.Add(1)
          go downloadPart(url,filename,i,byteStart,byteEnd)
        }
      }
    }
    wg.Wait()
    mergeFiles(filename,*noOfFiles)
    clearFiles(filename,*noOfFiles)
    reader,_ := ioutil.ReadFile(filename)
    log.Println(len(reader))
}

func DownloadSingle(url string){
    filename := getFilenameFromUrl(url)
    client := &http.Client{}
    req, _ := http.NewRequest("GET",url, nil)
    resp, err := client.Do(req)
    if err != nil {
      log.Fatal(err)
    }
    reader, err := ioutil.ReadAll(resp.Body)
    if err != nil {
      log.Fatal(err)
    }
    log.Println(len(reader))
    err = ioutil.WriteFile(filename, reader,0666)
    if err != nil {
      log.Fatal(err)
    }
}
<|endoftext|>


<commit_before>package http

import (
	"net/http"
	"fmt"
	"time"
	log "github.com/Sirupsen/logrus"
	"github.com/davepgreene/turnstile/store"
	"github.com/davepgreene/turnstile/utils"
	"github.com/gorilla/mux"
	"github.com/meatballhat/negroni-logrus"
	"github.com/spf13/viper"
	"github.com/thoas/stats"
	"github.com/urfave/negroni"
	"gopkg.in/tylerb/graceful.v1"
	"crypto"
	"github.com/davepgreene/turnstile/config"
	"github.com/davepgreene/turnstile/proxy"
)

const (
	// MaxRequestSize is the maximum accepted request size. This is to prevent
	// a denial of service attack where no Content-Length is provided and the server
	// is fed ever more data until it exhausts memory.
	MaxRequestSize = 32 * 1024 * 1024
)

// Handler returns an http.Handler for the API.
func Handler() error {
	db := db()
	r := mux.NewRouter()
	statsMiddleware := stats.New()
	r.HandleFunc("/stats", newAdminHandler(statsMiddleware).ServeHTTP)

	// Add middleware handlers
	n := negroni.New()

	// Add recovery handler that logs
	recovery := negroni.NewRecovery()
	recovery.PrintStack = false
	n.Use(recovery)

	if viper.GetBool("log.requests") {
		n.Use(negronilogrus.NewCustomMiddleware(utils.GetLogLevel(), utils.GetLogFormatter(), "requests"))
	}

	n.Use(statsMiddleware)

	// Collect some metrics about incoming and active requests
	n.Use(negroni.HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		utils.Metrics().Incr("request.incoming", nil, 1)
		utils.Metrics().Gauge("request.active", 1, nil, 1)
		next(rw, r)
	}))

	// Correlation id middleware
	if viper.GetBool("correlation.enable") {
		n.Use(negroni.HandlerFunc(correlationMiddleware(viper.GetStringMap("correlation"))))
	}

	// Validate headers
	n.Use(negroni.HandlerFunc(validate(viper.GetInt("local.skew"))))

	// Validate the digest header
	algorithm := viper.Get("local.algorithm")
	n.Use(negroni.HandlerFunc(digest(algorithm.(crypto.Hash))))
	n.Use(negroni.HandlerFunc(authorization))
	n.Use(negroni.HandlerFunc(signature(db, algorithm.(crypto.Hash))))

	// All checks passed, forward the request
	forwardConn := fmt.Sprintf("%s%s:%d", viper.GetString("service.protocol"), viper.GetString("service.hostname"), viper.GetInt("service.port"))
	p := proxy.New(forwardConn)
	r.PathPrefix("/").HandlerFunc(p.Handle)

	n.UseHandler(r)

	// Set up connection
	conn := fmt.Sprintf("%s:%d", viper.GetString("listen.bind"), viper.GetInt("listen.port"))
	log.Infof("Listening on %s", conn)

	// Bombs away!
	return server(conn, n).ListenAndServe()
}

func server(conn string, handler http.Handler) *graceful.Server {
	return &graceful.Server{
		Timeout: 10 * time.Second,
		Server: &http.Server{
			Addr:    conn,
			Handler: handler,
		},
	}
}

func db() store.Store {
	// Create DB
	storeType := "file"
	if viper.GetBool("local.db.propsd") {
		storeType = "propsd"
	}

	local := config.Local()["db"].(map[string]interface{})
	db, err := store.CreateStore(storeType, local)
	if err != nil {
		// If we have an invalid store, dump out before we start the server
		panic(err)
	}
	log.Infof("Using authentication controller with database: %s", db.Path())
	return db
}
<commit_msg>Fixed bug where a propsd store would never be used because of the layering bug in Viper.<commit_after>package http

import (
	"net/http"
	"fmt"
	"time"
	log "github.com/Sirupsen/logrus"
	"github.com/davepgreene/turnstile/store"
	"github.com/davepgreene/turnstile/utils"
	"github.com/gorilla/mux"
	"github.com/meatballhat/negroni-logrus"
	"github.com/spf13/viper"
	"github.com/thoas/stats"
	"github.com/urfave/negroni"
	"gopkg.in/tylerb/graceful.v1"
	"crypto"
	"github.com/davepgreene/turnstile/config"
	"github.com/davepgreene/turnstile/proxy"
)

const (
	// MaxRequestSize is the maximum accepted request size. This is to prevent
	// a denial of service attack where no Content-Length is provided and the server
	// is fed ever more data until it exhausts memory.
	MaxRequestSize = 32 * 1024 * 1024
)

// Handler returns an http.Handler for the API.
func Handler() error {
	db := db()
	r := mux.NewRouter()
	statsMiddleware := stats.New()
	r.HandleFunc("/stats", newAdminHandler(statsMiddleware).ServeHTTP)

	// Add middleware handlers
	n := negroni.New()

	// Add recovery handler that logs
	recovery := negroni.NewRecovery()
	recovery.PrintStack = false
	n.Use(recovery)

	if viper.GetBool("log.requests") {
		n.Use(negronilogrus.NewCustomMiddleware(utils.GetLogLevel(), utils.GetLogFormatter(), "requests"))
	}

	n.Use(statsMiddleware)

	// Collect some metrics about incoming and active requests
	n.Use(negroni.HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		utils.Metrics().Incr("request.incoming", nil, 1)
		utils.Metrics().Gauge("request.active", 1, nil, 1)
		next(rw, r)
	}))

	// Correlation id middleware
	if viper.GetBool("correlation.enable") {
		n.Use(negroni.HandlerFunc(correlationMiddleware(viper.GetStringMap("correlation"))))
	}

	// Validate headers
	n.Use(negroni.HandlerFunc(validate(viper.GetInt("local.skew"))))

	// Validate the digest header
	algorithm := viper.Get("local.algorithm")
	n.Use(negroni.HandlerFunc(digest(algorithm.(crypto.Hash))))
	n.Use(negroni.HandlerFunc(authorization))
	n.Use(negroni.HandlerFunc(signature(db, algorithm.(crypto.Hash))))

	// All checks passed, forward the request
	forwardConn := fmt.Sprintf("%s%s:%d", viper.GetString("service.protocol"), viper.GetString("service.hostname"), viper.GetInt("service.port"))
	p := proxy.New(forwardConn)
	r.PathPrefix("/").HandlerFunc(p.Handle)

	n.UseHandler(r)

	// Set up connection
	conn := fmt.Sprintf("%s:%d", viper.GetString("listen.bind"), viper.GetInt("listen.port"))
	log.Infof("Listening on %s", conn)

	// Bombs away!
	return server(conn, n).ListenAndServe()
}

func server(conn string, handler http.Handler) *graceful.Server {
	return &graceful.Server{
		Timeout: 10 * time.Second,
		Server: &http.Server{
			Addr:    conn,
			Handler: handler,
		},
	}
}

func db() store.Store {
	local := config.Local()["db"].(map[string]interface{})
	// Create DB
	storeType := "file"
	if local["propsd"].(bool) == true {
		storeType = "propsd"
	}

	db, err := store.CreateStore(storeType, local)
	if err != nil {
		// If we have an invalid store, dump out before we start the server
		panic(err)
	}
	log.Infof("Using authentication controller with database: %s", db.Path())
	return db
}
<|endoftext|>


<commit_before>package orient

import (
	"bytes"
	"fmt"
	"time"

	//	"database/sql/driver"
	"reflect"
	"strings"
)

var (
	_ OIdentifiable        = (*Document)(nil)
	_ DocumentSerializable = (*Document)(nil)
	_ MapSerializable      = (*Document)(nil)
	_ ORecord              = (*Document)(nil)
)

// DocEntry is a generic data holder that goes in Documents.
type DocEntry struct {
	Name  string
	Type  OType
	Value interface{}
}

func (fld *DocEntry) String() string {
	if id, ok := fld.Value.(OIdentifiable); ok {
		return fmt.Sprintf("{%s(%s): %v}", fld.Name, fld.Type, id.GetIdentity())
	}
	return fmt.Sprintf("{%s(%s): %v}", fld.Name, fld.Type, fld.Value)
}

type Document struct {
	BytesRecord
	serialized  bool
	fieldsOrder []string // field names in the order they were added to the Document
	fields      map[string]*DocEntry
	classname   string // TODO: probably needs to change *OClass (once that is built)
	dirty       bool
	ser         RecordSerializer
}

func (doc *Document) ClassName() string { return doc.classname }

// NewDocument should be called to create new Document objects,
// since some internal data structures need to be initialized
// before the Document is ready to use.
func NewDocument(className string) *Document {
	doc := NewEmptyDocument()
	doc.classname = className
	return doc
}

// TODO: have this replace NewDocument and change NewDocument to take RID and Version (???)
func NewEmptyDocument() *Document {
	return &Document{
		BytesRecord: BytesRecord{
			RID:  NewEmptyRID(),
			Vers: -1,
		},
		fields: make(map[string]*DocEntry),
		ser:    GetDefaultRecordSerializer(),
	}
}

func (doc *Document) ensureDecoded() error {
	if doc == nil {
		return fmt.Errorf("nil document")
	}
	if !doc.serialized {
		return nil
	}
	o, err := doc.ser.FromStream(doc.BytesRecord.Data)
	if err != nil {
		return err
	}
	ndoc, ok := o.(*Document)
	if !ok {
		return fmt.Errorf("expected document, got %T", o)
	}
	doc.classname = ndoc.classname
	doc.fields = ndoc.fields
	doc.fieldsOrder = ndoc.fieldsOrder
	doc.serialized = false
	return nil
}

func (doc *Document) Content() ([]byte, error) {
	// TODO: can track field changes and invalidate content if necessary - no need to serialize each time
	if doc.serialized {
		return doc.BytesRecord.Content()
	}
	buf := bytes.NewBuffer(nil)
	if err := doc.ser.ToStream(buf, doc); err != nil {
		return nil, err
	}
	doc.BytesRecord.Data = buf.Bytes()
	return doc.BytesRecord.Content()
}

func (doc *Document) GetIdentity() RID {
	if doc == nil {
		return NewEmptyRID()
	}
	return doc.BytesRecord.GetIdentity()
}

func (doc *Document) GetRecord() interface{} {
	if doc == nil {
		return nil
	}
	return doc
}

// FieldNames returns the names of all the fields currently in this Document
// in "entry order". These fields may not have already been committed to the database.
func (doc *Document) FieldNames() []string {
	doc.ensureDecoded()
	names := make([]string, len(doc.fieldsOrder))
	copy(names, doc.fieldsOrder)
	return names
}

func (doc *Document) Fields() map[string]*DocEntry {
	doc.ensureDecoded()
	return doc.fields // TODO: copy map?
}

// FieldsArray return the OField objects in the Document in "entry order".
// There is some overhead to getting them in entry order, so if you
// don't care about that order, just access the Fields field of the
// Document struct directly.
func (doc *Document) FieldsArray() []*DocEntry {
	doc.ensureDecoded()
	fields := make([]*DocEntry, len(doc.fieldsOrder))
	for i, name := range doc.fieldsOrder {
		fields[i] = doc.fields[name]
	}
	return fields
}

// GetFieldByName looks up the OField in this document with the specified field.
// If no field is found with that name, nil is returned.
func (doc *Document) GetField(fname string) *DocEntry {
	doc.ensureDecoded()
	return doc.fields[fname]
}

// AddField adds a fully created field directly rather than by some of its
// attributes, as the other "Field" methods do.
// The same *Document is returned to allow call chaining.
func (doc *Document) AddField(name string, field *DocEntry) *Document {
	doc.ensureDecoded()
	doc.fields[name] = field
	doc.fieldsOrder = append(doc.fieldsOrder, name)
	doc.dirty = true
	return doc
}

func (doc *Document) SetDirty(b bool) {
	doc.dirty = b
}

// SetField is used to add a new field to a document. This will usually be done just
// before calling Save and sending it to the database.  The field type will be inferred
// via type switch analysis on `val`.  Use FieldWithType to specify the type directly.
// The same *Document is returned to allow call chaining.
func (doc *Document) SetField(name string, val interface{}) *Document {
	doc.ensureDecoded()
	return doc.SetFieldWithType(name, val, OTypeForValue(val))
}

// FieldWithType is used to add a new field to a document. This will usually be done just
// before calling Save and sending it to the database. The `fieldType` must correspond
// one of the OrientDB type in the schema pkg constants.  It will follow the same list
// as: https://github.com/orientechnologies/orientdb/wiki/Types
// The same *Document is returned to allow call chaining.
func (doc *Document) SetFieldWithType(name string, val interface{}, fieldType OType) *Document {
	doc.ensureDecoded()
	fld := &DocEntry{
		Name:  name,
		Value: val,
		Type:  fieldType,
	}

	if fieldType == DATE {
		fld.Value = adjustDateToMidnight(val)
	} else if fieldType == DATETIME {
		fld.Value = roundDateTimeToMillis(val)
	}

	return doc.AddField(name, fld)
}

func (doc *Document) RawContainsField(name string) bool {
	doc.ensureDecoded()
	return doc != nil && doc.fields[name] != nil
}

func (doc *Document) RawSetField(name string, val interface{}, fieldType OType) {
	doc.SetFieldWithType(name, val, fieldType) // TODO: implement in a right way
}

// roundDateTimeToMillis zeros out the micro and nanoseconds of a
// time.Time object in order to match the precision with which
// the OrientDB stores DATETIME values
func roundDateTimeToMillis(val interface{}) interface{} {
	tm, ok := val.(time.Time)
	if !ok {
		// if the type is wrong, we will flag it as an error when the user tries
		// to save it, rather than here while buidling the document
		return val
	}

	return tm.Round(time.Millisecond)
}

// adjustDateToMidnight zeros out the hour, minute, second, etc.
// to set the time of a DATE to midnight.  This matches the
// precision with which the OrientDB stores DATE values.
func adjustDateToMidnight(val interface{}) interface{} {
	tm, ok := val.(time.Time)
	if !ok {
		// if the type is wrong, we will flag it as an error when the user tries
		// to save it, rather than here while buidling the document
		return val
	}
	tmMidnight := time.Date(tm.Year(), tm.Month(), tm.Day(), 0, 0, 0, 0, tm.Location())
	return interface{}(tmMidnight)
}

func (doc *Document) String() string {
	class := doc.classname
	if class == "" {
		class = "<nil>"
	}
	if doc.serialized {
		return fmt.Sprintf("Document{Class: %s, RID: %s, Vers: %d, Fields: [serialized]}",
			class, doc.RID, doc.Vers)
	}
	buf := new(bytes.Buffer)
	_, err := buf.WriteString(fmt.Sprintf("Document{Class: %s, RID: %s, Vers: %d, Fields: [\n",
		class, doc.RID, doc.Vers))
	if err != nil {
		panic(err)
	}

	for _, fld := range doc.fields {
		_, err = buf.WriteString(fmt.Sprintf("  %s,\n", fld.String()))
		if err != nil {
			panic(err)
		}
	}

	buf.WriteString("]}\n")
	return buf.String()
}

func (doc *Document) ToMap() (map[string]interface{}, error) {
	if doc == nil {
		return nil, nil
	}
	if err := doc.ensureDecoded(); err != nil {
		return nil, err
	}
	out := make(map[string]interface{}, len(doc.fields))
	for name, fld := range doc.fields {
		out[name] = fld.Value
	}
	if doc.classname != "" {
		out["@class"] = doc.classname
	}
	if doc.RID.IsPersistent() { // TODO: is this correct?
		out["@rid"] = doc.RID
	}
	return out, nil
}

func (doc *Document) FillClassNameIfNeeded(name string) {
	if doc.classname == "" {
		doc.SetClassNameIfExists(name)
	}
}

func (doc *Document) SetClassNameIfExists(name string) {
	// TODO: implement class lookup
	//	_immutableClazz = null;
	//	_immutableSchemaVersion = -1;

	doc.classname = name
	if name == "" {
		return
	}

	//    final ODatabaseDocument db = getDatabaseIfDefined();
	//    if (db != null) {
	//      final OClass _clazz = ((OMetadataInternal) db.getMetadata()).getImmutableSchemaSnapshot().getClass(iClassName);
	//      if (_clazz != null) {
	//        _className = _clazz.getName();
	//        convertFieldsToClass(_clazz);
	//      }
	//    }
}

// SetSerializer sets RecordSerializer for encoding/decoding a Document
func (doc *Document) SetSerializer(ser RecordSerializer) {
	doc.ser = ser
}
func (doc *Document) Fill(rid RID, version int, content []byte) error {
	doc.serialized = doc.serialized || doc.BytesRecord.Data == nil || bytes.Compare(content, doc.BytesRecord.Data) != 0
	return doc.BytesRecord.Fill(rid, version, content)
}
func (doc *Document) RecordType() RecordType { return RecordTypeDocument }

// ToDocument implement DocumentSerializable interface. In this case, Document just returns itself.
func (doc *Document) ToDocument() (*Document, error) {
	return doc, nil
}

// ToStruct fills provided struct with content of a Document. Argument must be a pointer to structure.
func (doc *Document) ToStruct(o interface{}) error {
	mp, err := doc.ToMap()
	if err != nil {
		return err
	}
	return mapToStruct(mp, o)
}

func (doc *Document) setFieldsFrom(rv reflect.Value) error {
	switch rv.Kind() {
	case reflect.Struct:
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			fld := rt.Field(i)
			if !isExported(fld.Name) {
				continue
			}
			name := fld.Name
			tags := strings.Split(fld.Tag.Get(TagName), ",")
			if tags[0] == "-" {
				continue
			}
			if tags[0] != "" {
				name = tags[0]
			}
			squash := (len(tags) > 1 && tags[1] == "squash") // TODO: change default behavior to squash if field is anonymous
			if squash {
				if err := doc.setFieldsFrom(rv.Field(i)); err != nil {
					return fmt.Errorf("field '%s': %s", name, err)
				}
			} else {
				doc.SetField(name, rv.Field(i).Interface())
			}
		}
		return nil
	case reflect.Map:
		for _, key := range rv.MapKeys() {
			doc.SetField(fmt.Sprint(key.Interface()), rv.MapIndex(key).Interface())
		}
		return nil
	default:
		return fmt.Errorf("only maps and structs are supported, got: %T", rv.Interface())
	}
}

// From sets Document fields to values provided in argument (which can be a map or a struct).
//
// From uses TagName field tag to determine field name and conversion parameters.
// For now it supports only one special tag parameter: ",squash" which can be used to inline fields into parent struct.
func (doc *Document) From(o interface{}) error {
	// TODO: clear fields and serialized data
	if o == nil {
		return nil
	}
	rv := reflect.ValueOf(o)
	if rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
		rv = rv.Elem()
	}
	return doc.setFieldsFrom(rv)
}

/*
// Implements database/sql.Scanner interface
func (doc *Document) Scan(src interface{}) error {
	switch v := src.(type) {
	case *Document:
		*doc = *v
	default:
		return fmt.Errorf("Document: cannot convert from %T to %T", src, doc)
	}
	return nil
}

// Implements database/sql/driver.Valuer interface
// TODO: haven't detected when this is called yet (probably when serializing Document for insertion into DB??)
func (doc *Document) Value() (driver.Value, error) {
	if glog.V(10) {
		glog.Infoln("** Document.Value")
	}
	return []byte(`{"b": 2}`), nil // FIXME: bogus
}

// Implements database/sql/driver.ValueConverter interface
// TODO: haven't detected when this is called yet
func (doc *Document) ConvertValue(v interface{}) (driver.Value, error) {
	if glog.V(10) {
		glog.Infof("** Document.ConvertValue: %T: %v", v, v)
	}
	return []byte(`{"a": 1}`), nil // FIXME: bogus
}*/
<commit_msg>Add function to create document from given RID<commit_after>package orient

import (
	"bytes"
	"fmt"
	"time"

	//	"database/sql/driver"
	"reflect"
	"strings"
)

var (
	_ OIdentifiable        = (*Document)(nil)
	_ DocumentSerializable = (*Document)(nil)
	_ MapSerializable      = (*Document)(nil)
	_ ORecord              = (*Document)(nil)
)

// DocEntry is a generic data holder that goes in Documents.
type DocEntry struct {
	Name  string
	Type  OType
	Value interface{}
}

func (fld *DocEntry) String() string {
	if id, ok := fld.Value.(OIdentifiable); ok {
		return fmt.Sprintf("{%s(%s): %v}", fld.Name, fld.Type, id.GetIdentity())
	}
	return fmt.Sprintf("{%s(%s): %v}", fld.Name, fld.Type, fld.Value)
}

type Document struct {
	BytesRecord
	serialized  bool
	fieldsOrder []string // field names in the order they were added to the Document
	fields      map[string]*DocEntry
	classname   string // TODO: probably needs to change *OClass (once that is built)
	dirty       bool
	ser         RecordSerializer
}

func (doc *Document) ClassName() string { return doc.classname }

// NewDocument should be called to create new Document objects,
// since some internal data structures need to be initialized
// before the Document is ready to use.
func NewDocument(className string) *Document {
	doc := NewEmptyDocument()
	doc.classname = className
	return doc
}

// NewEmptyDocument creates new empty document.
func NewEmptyDocument() *Document {
	return &Document{
		BytesRecord: BytesRecord{
			RID:  NewEmptyRID(),
			Vers: -1,
		},
		fields: make(map[string]*DocEntry),
		ser:    GetDefaultRecordSerializer(),
	}
}

// NewDocumentFromRID creates new empty document with given RID.
func NewDocumentFromRID(rid RID) *Document {
	return &Document{
		BytesRecord: BytesRecord{
			RID:  rid,
			Vers: -1,
		},
		fields: make(map[string]*DocEntry),
		ser:    GetDefaultRecordSerializer(),
	}
}

func (doc *Document) ensureDecoded() error {
	if doc == nil {
		return fmt.Errorf("nil document")
	}
	if !doc.serialized {
		return nil
	}
	o, err := doc.ser.FromStream(doc.BytesRecord.Data)
	if err != nil {
		return err
	}
	ndoc, ok := o.(*Document)
	if !ok {
		return fmt.Errorf("expected document, got %T", o)
	}
	doc.classname = ndoc.classname
	doc.fields = ndoc.fields
	doc.fieldsOrder = ndoc.fieldsOrder
	doc.serialized = false
	return nil
}

func (doc *Document) Content() ([]byte, error) {
	// TODO: can track field changes and invalidate content if necessary - no need to serialize each time
	if doc.serialized {
		return doc.BytesRecord.Content()
	}
	buf := bytes.NewBuffer(nil)
	if err := doc.ser.ToStream(buf, doc); err != nil {
		return nil, err
	}
	doc.BytesRecord.Data = buf.Bytes()
	return doc.BytesRecord.Content()
}

func (doc *Document) GetIdentity() RID {
	if doc == nil {
		return NewEmptyRID()
	}
	return doc.BytesRecord.GetIdentity()
}

func (doc *Document) GetRecord() interface{} {
	if doc == nil {
		return nil
	}
	return doc
}

// FieldNames returns the names of all the fields currently in this Document
// in "entry order". These fields may not have already been committed to the database.
func (doc *Document) FieldNames() []string {
	doc.ensureDecoded()
	names := make([]string, len(doc.fieldsOrder))
	copy(names, doc.fieldsOrder)
	return names
}

func (doc *Document) Fields() map[string]*DocEntry {
	doc.ensureDecoded()
	return doc.fields // TODO: copy map?
}

// FieldsArray return the OField objects in the Document in "entry order".
// There is some overhead to getting them in entry order, so if you
// don't care about that order, just access the Fields field of the
// Document struct directly.
func (doc *Document) FieldsArray() []*DocEntry {
	doc.ensureDecoded()
	fields := make([]*DocEntry, len(doc.fieldsOrder))
	for i, name := range doc.fieldsOrder {
		fields[i] = doc.fields[name]
	}
	return fields
}

// GetFieldByName looks up the OField in this document with the specified field.
// If no field is found with that name, nil is returned.
func (doc *Document) GetField(fname string) *DocEntry {
	doc.ensureDecoded()
	return doc.fields[fname]
}

// AddField adds a fully created field directly rather than by some of its
// attributes, as the other "Field" methods do.
// The same *Document is returned to allow call chaining.
func (doc *Document) AddField(name string, field *DocEntry) *Document {
	doc.ensureDecoded()
	doc.fields[name] = field
	doc.fieldsOrder = append(doc.fieldsOrder, name)
	doc.dirty = true
	return doc
}

func (doc *Document) SetDirty(b bool) {
	doc.dirty = b
}

// SetField is used to add a new field to a document. This will usually be done just
// before calling Save and sending it to the database.  The field type will be inferred
// via type switch analysis on `val`.  Use FieldWithType to specify the type directly.
// The same *Document is returned to allow call chaining.
func (doc *Document) SetField(name string, val interface{}) *Document {
	doc.ensureDecoded()
	return doc.SetFieldWithType(name, val, OTypeForValue(val))
}

// FieldWithType is used to add a new field to a document. This will usually be done just
// before calling Save and sending it to the database. The `fieldType` must correspond
// one of the OrientDB type in the schema pkg constants.  It will follow the same list
// as: https://github.com/orientechnologies/orientdb/wiki/Types
// The same *Document is returned to allow call chaining.
func (doc *Document) SetFieldWithType(name string, val interface{}, fieldType OType) *Document {
	doc.ensureDecoded()
	fld := &DocEntry{
		Name:  name,
		Value: val,
		Type:  fieldType,
	}

	if fieldType == DATE {
		fld.Value = adjustDateToMidnight(val)
	} else if fieldType == DATETIME {
		fld.Value = roundDateTimeToMillis(val)
	}

	return doc.AddField(name, fld)
}

func (doc *Document) RawContainsField(name string) bool {
	doc.ensureDecoded()
	return doc != nil && doc.fields[name] != nil
}

func (doc *Document) RawSetField(name string, val interface{}, fieldType OType) {
	doc.SetFieldWithType(name, val, fieldType) // TODO: implement in a right way
}

// roundDateTimeToMillis zeros out the micro and nanoseconds of a
// time.Time object in order to match the precision with which
// the OrientDB stores DATETIME values
func roundDateTimeToMillis(val interface{}) interface{} {
	tm, ok := val.(time.Time)
	if !ok {
		// if the type is wrong, we will flag it as an error when the user tries
		// to save it, rather than here while buidling the document
		return val
	}

	return tm.Round(time.Millisecond)
}

// adjustDateToMidnight zeros out the hour, minute, second, etc.
// to set the time of a DATE to midnight.  This matches the
// precision with which the OrientDB stores DATE values.
func adjustDateToMidnight(val interface{}) interface{} {
	tm, ok := val.(time.Time)
	if !ok {
		// if the type is wrong, we will flag it as an error when the user tries
		// to save it, rather than here while buidling the document
		return val
	}
	tmMidnight := time.Date(tm.Year(), tm.Month(), tm.Day(), 0, 0, 0, 0, tm.Location())
	return interface{}(tmMidnight)
}

func (doc *Document) String() string {
	class := doc.classname
	if class == "" {
		class = "<nil>"
	}
	if doc.serialized {
		return fmt.Sprintf("Document{Class: %s, RID: %s, Vers: %d, Fields: [serialized]}",
			class, doc.RID, doc.Vers)
	}
	buf := new(bytes.Buffer)
	_, err := buf.WriteString(fmt.Sprintf("Document{Class: %s, RID: %s, Vers: %d, Fields: [\n",
		class, doc.RID, doc.Vers))
	if err != nil {
		panic(err)
	}

	for _, fld := range doc.fields {
		_, err = buf.WriteString(fmt.Sprintf("  %s,\n", fld.String()))
		if err != nil {
			panic(err)
		}
	}

	buf.WriteString("]}\n")
	return buf.String()
}

func (doc *Document) ToMap() (map[string]interface{}, error) {
	if doc == nil {
		return nil, nil
	}
	if err := doc.ensureDecoded(); err != nil {
		return nil, err
	}
	out := make(map[string]interface{}, len(doc.fields))
	for name, fld := range doc.fields {
		out[name] = fld.Value
	}
	if doc.classname != "" {
		out["@class"] = doc.classname
	}
	if doc.RID.IsPersistent() { // TODO: is this correct?
		out["@rid"] = doc.RID
	}
	return out, nil
}

func (doc *Document) FillClassNameIfNeeded(name string) {
	if doc.classname == "" {
		doc.SetClassNameIfExists(name)
	}
}

func (doc *Document) SetClassNameIfExists(name string) {
	// TODO: implement class lookup
	//	_immutableClazz = null;
	//	_immutableSchemaVersion = -1;

	doc.classname = name
	if name == "" {
		return
	}

	//    final ODatabaseDocument db = getDatabaseIfDefined();
	//    if (db != null) {
	//      final OClass _clazz = ((OMetadataInternal) db.getMetadata()).getImmutableSchemaSnapshot().getClass(iClassName);
	//      if (_clazz != null) {
	//        _className = _clazz.getName();
	//        convertFieldsToClass(_clazz);
	//      }
	//    }
}

// SetSerializer sets RecordSerializer for encoding/decoding a Document
func (doc *Document) SetSerializer(ser RecordSerializer) {
	doc.ser = ser
}
func (doc *Document) Fill(rid RID, version int, content []byte) error {
	doc.serialized = doc.serialized || doc.BytesRecord.Data == nil || bytes.Compare(content, doc.BytesRecord.Data) != 0
	return doc.BytesRecord.Fill(rid, version, content)
}
func (doc *Document) RecordType() RecordType { return RecordTypeDocument }

// ToDocument implement DocumentSerializable interface. In this case, Document just returns itself.
func (doc *Document) ToDocument() (*Document, error) {
	return doc, nil
}

// ToStruct fills provided struct with content of a Document. Argument must be a pointer to structure.
func (doc *Document) ToStruct(o interface{}) error {
	mp, err := doc.ToMap()
	if err != nil {
		return err
	}
	return mapToStruct(mp, o)
}

func (doc *Document) setFieldsFrom(rv reflect.Value) error {
	switch rv.Kind() {
	case reflect.Struct:
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			fld := rt.Field(i)
			if !isExported(fld.Name) {
				continue
			}
			name := fld.Name
			tags := strings.Split(fld.Tag.Get(TagName), ",")
			if tags[0] == "-" {
				continue
			}
			if tags[0] != "" {
				name = tags[0]
			}
			squash := (len(tags) > 1 && tags[1] == "squash") // TODO: change default behavior to squash if field is anonymous
			if squash {
				if err := doc.setFieldsFrom(rv.Field(i)); err != nil {
					return fmt.Errorf("field '%s': %s", name, err)
				}
			} else {
				doc.SetField(name, rv.Field(i).Interface())
			}
		}
		return nil
	case reflect.Map:
		for _, key := range rv.MapKeys() {
			doc.SetField(fmt.Sprint(key.Interface()), rv.MapIndex(key).Interface())
		}
		return nil
	default:
		return fmt.Errorf("only maps and structs are supported, got: %T", rv.Interface())
	}
}

// From sets Document fields to values provided in argument (which can be a map or a struct).
//
// From uses TagName field tag to determine field name and conversion parameters.
// For now it supports only one special tag parameter: ",squash" which can be used to inline fields into parent struct.
func (doc *Document) From(o interface{}) error {
	// TODO: clear fields and serialized data
	if o == nil {
		return nil
	}
	rv := reflect.ValueOf(o)
	if rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
		rv = rv.Elem()
	}
	return doc.setFieldsFrom(rv)
}

/*
// Implements database/sql.Scanner interface
func (doc *Document) Scan(src interface{}) error {
	switch v := src.(type) {
	case *Document:
		*doc = *v
	default:
		return fmt.Errorf("Document: cannot convert from %T to %T", src, doc)
	}
	return nil
}

// Implements database/sql/driver.Valuer interface
// TODO: haven't detected when this is called yet (probably when serializing Document for insertion into DB??)
func (doc *Document) Value() (driver.Value, error) {
	if glog.V(10) {
		glog.Infoln("** Document.Value")
	}
	return []byte(`{"b": 2}`), nil // FIXME: bogus
}

// Implements database/sql/driver.ValueConverter interface
// TODO: haven't detected when this is called yet
func (doc *Document) ConvertValue(v interface{}) (driver.Value, error) {
	if glog.V(10) {
		glog.Infof("** Document.ConvertValue: %T: %v", v, v)
	}
	return []byte(`{"a": 1}`), nil // FIXME: bogus
}*/
<|endoftext|>


<commit_before>package admin

import (
	"github.com/kataras/iris"
	"jiacrontab/models"
)

// GetNodeList 获得任务节点列表
// 支持获得所属分组节点，指定分组节点（超级管理员）
func GetNodeList(c iris.Context) {
	var (
		ctx      = wrapCtx(c)
		err      error
		nodeList []models.Node
		reqBody  GetNodeListReqParams
		count    int
	)

	if err = ctx.parseClaimsFromToken(); err != nil {
		ctx.respJWTError(err)
		return
	}

	if err = ctx.Valid(&reqBody); err != nil {
		ctx.respParamError(err)
		return
	}

	if reqBody.QueryGroupID != ctx.claims.GroupID && !ctx.isSuper() {
		ctx.respNotAllowed()
		return
	}

	err = models.DB().Preload("Group").Where("group_id=?", reqBody.QueryGroupID).Offset(reqBody.Page - 1).Limit(reqBody.Pagesize).Find(&nodeList).Error
	models.DB().Model(&models.Node{}).Where("group_id=?", reqBody.QueryGroupID).Count(&count)

	if err != nil {
		ctx.respBasicError(err)
		return
	}

	ctx.respSucc("", map[string]interface{}{
		"list":     nodeList,
		"total":    count,
		"page":     reqBody.Page,
		"pagesize": reqBody.Pagesize,
	})
}

// DeleteNode 删除分组内节点
// 仅超级管理员有权限
func DeleteNode(c iris.Context) {
	var (
		err     error
		ctx     = wrapCtx(c)
		reqBody DeleteNodeReqParams
		group   models.Group
		node    models.Node
	)

	if err = ctx.Valid(&reqBody); err != nil {
		ctx.respParamError(err)
	}

	// 普通用户不允许删除节点
	if !ctx.isSuper() {
		ctx.respNotAllowed()
		return
	}

	if err := models.DB().Take(&group, "id=?", reqBody.GroupID).Error; err != nil {
		ctx.respDBError(err)
		return
	}

	if err = node.Delete(reqBody.GroupID, reqBody.Addr); err != nil {
		ctx.respDBError(err)
		return
	}

	ctx.pubEvent(node.Name, event_DelNodeDesc, reqBody.Addr, reqBody)
	ctx.respSucc("", nil)
}

// GroupNode 超级管理员为node分组
// 分组不存在时自动创建分组
// copy超级管理员分组中的节点到新的分组
func GroupNode(c iris.Context) {
	var (
		err     error
		ctx     = wrapCtx(c)
		reqBody GroupNodeReqParams
		node    models.Node
	)

	if !ctx.isSuper() {
		ctx.respNotAllowed()
		return
	}

	if err = ctx.Valid(&reqBody); err != nil {
		ctx.respParamError(err)
		return
	}

	if err = node.GroupNode(reqBody.Addr, reqBody.TargetGroupID,
		reqBody.TargetNodeName, reqBody.TargetGroupName); err != nil {
		ctx.respBasicError(err)
		return
	}

	ctx.pubEvent(reqBody.TargetNodeName, event_GroupNode, reqBody.Addr, reqBody)
	ctx.respSucc("", nil)
}
<commit_msg>update node list<commit_after>package admin

import (
	"github.com/kataras/iris"
	"jiacrontab/models"
)

// GetNodeList 获得任务节点列表
// 支持获得所属分组节点，指定分组节点（超级管理员）
func GetNodeList(c iris.Context) {
	var (
		ctx      = wrapCtx(c)
		err      error
		nodeList []models.Node
		reqBody  GetNodeListReqParams
		count    int
	)

	if err = ctx.parseClaimsFromToken(); err != nil {
		ctx.respJWTError(err)
		return
	}

	if err = ctx.Valid(&reqBody); err != nil {
		ctx.respParamError(err)
		return
	}

	if reqBody.QueryGroupID != ctx.claims.GroupID && !ctx.isSuper() {
		ctx.respNotAllowed()
		return
	}

	err = models.DB().Preload("Group").Where("group_id=?", reqBody.QueryGroupID).Offset(reqBody.Page - 1).Order("id desc").Limit(reqBody.Pagesize).Find(&nodeList).Error
	models.DB().Model(&models.Node{}).Where("group_id=?", reqBody.QueryGroupID).Count(&count)

	if err != nil {
		ctx.respBasicError(err)
		return
	}

	ctx.respSucc("", map[string]interface{}{
		"list":     nodeList,
		"total":    count,
		"page":     reqBody.Page,
		"pagesize": reqBody.Pagesize,
	})
}

// DeleteNode 删除分组内节点
// 仅超级管理员有权限
func DeleteNode(c iris.Context) {
	var (
		err     error
		ctx     = wrapCtx(c)
		reqBody DeleteNodeReqParams
		group   models.Group
		node    models.Node
	)

	if err = ctx.Valid(&reqBody); err != nil {
		ctx.respParamError(err)
	}

	// 普通用户不允许删除节点
	if !ctx.isSuper() {
		ctx.respNotAllowed()
		return
	}

	if err := models.DB().Take(&group, "id=?", reqBody.GroupID).Error; err != nil {
		ctx.respDBError(err)
		return
	}

	if err = node.Delete(reqBody.GroupID, reqBody.Addr); err != nil {
		ctx.respDBError(err)
		return
	}

	ctx.pubEvent(node.Name, event_DelNodeDesc, reqBody.Addr, reqBody)
	ctx.respSucc("", nil)
}

// GroupNode 超级管理员为node分组
// 分组不存在时自动创建分组
// copy超级管理员分组中的节点到新的分组
func GroupNode(c iris.Context) {
	var (
		err     error
		ctx     = wrapCtx(c)
		reqBody GroupNodeReqParams
		node    models.Node
	)

	if !ctx.isSuper() {
		ctx.respNotAllowed()
		return
	}

	if err = ctx.Valid(&reqBody); err != nil {
		ctx.respParamError(err)
		return
	}

	if err = node.GroupNode(reqBody.Addr, reqBody.TargetGroupID,
		reqBody.TargetNodeName, reqBody.TargetGroupName); err != nil {
		ctx.respBasicError(err)
		return
	}

	ctx.pubEvent(reqBody.TargetNodeName, event_GroupNode, reqBody.Addr, reqBody)
	ctx.respSucc("", nil)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"hash/fnv"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	pb "github.com/brotherlogic/cardserver/card"
)

const (
	datestr = "2006-01-02 15:04"
)

var (
	daysOfTheWeek = []string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
)

func getTime(timestr string) (time.Time, error) {
	t := time.Now()
	return time.ParseInLocation(datestr, timestr, t.Location())
}

func getUnixTime(timestr string) int64 {
	t, _ := getTime(timestr)
	return t.Unix()
}

type cronentry struct {
	time  *time.Time
	daily bool
	day   string
	text  string
	hash  string
}

// Cron the main cronentry holder
type Cron struct {
	crons   []cronentry
	last    time.Time
	dir     string
	written map[string]bool
}

// Init prepares a cron for use
func Init(dir string) *Cron {
	c := &Cron{}
	c.last = time.Unix(0, 0)
	c.dir = dir

	if _, err := os.Stat(c.dir); os.IsNotExist(err) {
		os.MkdirAll(c.dir, 0777)
	}

	c.loadhash()

	return c
}

// InitFromFile loads a cron from a given file
func InitFromFile(dir string, filename string) *Cron {
	c := Init(dir)

	file, _ := os.Open(filename)
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		c.loadline(scanner.Text())
	}

	//if err := scanner.Err(); err != nil {
	//	log.Fatal(err)
	//}

	return c
}

func hash(s string) string {
	h := fnv.New32a()
	h.Write([]byte(s))
	return strconv.Itoa(int(h.Sum32()))
}

func (c *Cron) loadhash() {
	c.written = make(map[string]bool)
	file, _ := os.Open(c.dir + "/hash")
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		c.written[scanner.Text()] = true
	}

}

func (c *Cron) clearhash() {
	c.written = make(map[string]bool)
	os.Remove(c.dir + "/hash")
}

func (c *Cron) writehash(card pb.Card) {
	if _, err := os.Stat(c.dir + "/hash"); os.IsNotExist(err) {
		os.Create(c.dir + "/hash")
	}

	f, _ := os.OpenFile(c.dir+"/hash", os.O_APPEND|os.O_WRONLY, 0600)
	defer f.Close()
	c.written[hash(card.String())] = true
	f.WriteString(hash(card.String()) + "\n")
}

func (c *Cron) isWritten(card pb.Card) bool {
	return c.written[hash(card.String())]
}

//GetCards gets the cards between the specified times
func (c *Cron) GetCards(ts time.Time, te time.Time) []*pb.Card {
	newindex := 0
	var cards []*pb.Card
	for i, entry := range c.crons {
		if entry.time != nil {
			if (entry.time.Before(te) || entry.time.Equal(te)) && entry.time.After(ts) {
				newindex = i + 1
				card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: entry.time.Unix(), Priority: -1, Hash: entry.hash}
				if !c.isWritten(card) {
					cards = append(cards, &card)
					c.writehash(card)
				}
			}
		} else if entry.day != "" {
			// Hack central
			stime := ts
			stime = stime.Add(-time.Hour * time.Duration(stime.Hour()))
			stime = stime.Add(-time.Minute * time.Duration(stime.Minute()))
			stime = stime.Add(-time.Second * time.Duration(stime.Second()))

			count := 1
			for stime.Before(te) {
				if stime.Format("Mon") == entry.day {
					card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: stime.Unix(), Priority: -1, Hash: entry.hash}
					if !c.isWritten(card) {
						cards = append(cards, &card)
						c.writehash(card)
					}
				}

				count++
				stime = stime.Add(time.Hour * 24)
			}
		} else if entry.daily {
			// Hack central
			stime := ts
			stime = stime.Add(-time.Hour * time.Duration(stime.Hour()))
			stime = stime.Add(-time.Minute * time.Duration(stime.Minute()))
			stime = stime.Add(-time.Second * time.Duration(stime.Second()))
			stime = stime.Add(time.Hour * time.Duration(5))

			count := 1
			for stime.Before(te) {
				card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: stime.Unix(), Priority: -1, Hash: entry.hash}
				log.Printf("WRITTEN = %v", c.isWritten(card))
				if !c.isWritten(card) {
					log.Printf("%v - %v", stime, entry.hash)
					cards = append(cards, &card)
					c.writehash(card)
				}

				count++
				stime = stime.Add(time.Hour * 24)
			}
		}
	}
	c.crons = c.crons[newindex:]
	return cards
}

func (c *Cron) logd() {
	log.Printf("LEN = %v", len(c.crons))
}

func matches(s string, strs []string) bool {
	for _, str := range strs {
		if s == str {
			return true
		}
	}
	return false
}

func (c *Cron) loadline(line string) {
	elems := strings.Split(line, "~")
	entry := cronentry{}
	if matches(elems[0], daysOfTheWeek) {
		entry.day = elems[0]
	} else if elems[0] == "Daily" {
		entry.daily = true
	} else {
		t, _ := getTime(elems[0])
		entry.time = &t
	}
	entry.text = elems[2] + "|" + elems[3]
	entry.hash = elems[1] + "-" + elems[4]

	c.crons = append(c.crons, entry)
}
<commit_msg>Cleared logs<commit_after>package main

import (
	"bufio"
	"hash/fnv"
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	pb "github.com/brotherlogic/cardserver/card"
)

const (
	datestr = "2006-01-02 15:04"
)

var (
	daysOfTheWeek = []string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
)

func getTime(timestr string) (time.Time, error) {
	t := time.Now()
	return time.ParseInLocation(datestr, timestr, t.Location())
}

func getUnixTime(timestr string) int64 {
	t, _ := getTime(timestr)
	return t.Unix()
}

type cronentry struct {
	time  *time.Time
	daily bool
	day   string
	text  string
	hash  string
}

// Cron the main cronentry holder
type Cron struct {
	crons   []cronentry
	last    time.Time
	dir     string
	written map[string]bool
}

// Init prepares a cron for use
func Init(dir string) *Cron {
	c := &Cron{}
	c.last = time.Unix(0, 0)
	c.dir = dir

	if _, err := os.Stat(c.dir); os.IsNotExist(err) {
		os.MkdirAll(c.dir, 0777)
	}

	c.loadhash()

	return c
}

// InitFromFile loads a cron from a given file
func InitFromFile(dir string, filename string) *Cron {
	c := Init(dir)

	file, _ := os.Open(filename)
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		c.loadline(scanner.Text())
	}

	//if err := scanner.Err(); err != nil {
	//	log.Fatal(err)
	//}

	return c
}

func hash(s string) string {
	h := fnv.New32a()
	h.Write([]byte(s))
	return strconv.Itoa(int(h.Sum32()))
}

func (c *Cron) loadhash() {
	c.written = make(map[string]bool)
	file, _ := os.Open(c.dir + "/hash")
	defer file.Close()
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		c.written[scanner.Text()] = true
	}

}

func (c *Cron) clearhash() {
	c.written = make(map[string]bool)
	os.Remove(c.dir + "/hash")
}

func (c *Cron) writehash(card pb.Card) {
	if _, err := os.Stat(c.dir + "/hash"); os.IsNotExist(err) {
		os.Create(c.dir + "/hash")
	}

	f, _ := os.OpenFile(c.dir+"/hash", os.O_APPEND|os.O_WRONLY, 0600)
	defer f.Close()
	c.written[hash(card.String())] = true
	f.WriteString(hash(card.String()) + "\n")
}

func (c *Cron) isWritten(card pb.Card) bool {
	return c.written[hash(card.String())]
}

//GetCards gets the cards between the specified times
func (c *Cron) GetCards(ts time.Time, te time.Time) []*pb.Card {
	newindex := 0
	var cards []*pb.Card
	for i, entry := range c.crons {
		if entry.time != nil {
			if (entry.time.Before(te) || entry.time.Equal(te)) && entry.time.After(ts) {
				newindex = i + 1
				card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: entry.time.Unix(), Priority: -1, Hash: entry.hash}
				if !c.isWritten(card) {
					cards = append(cards, &card)
					c.writehash(card)
				}
			}
		} else if entry.day != "" {
			// Hack central
			stime := ts
			stime = stime.Add(-time.Hour * time.Duration(stime.Hour()))
			stime = stime.Add(-time.Minute * time.Duration(stime.Minute()))
			stime = stime.Add(-time.Second * time.Duration(stime.Second()))

			count := 1
			for stime.Before(te) {
				if stime.Format("Mon") == entry.day {
					card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: stime.Unix(), Priority: -1, Hash: entry.hash}
					if !c.isWritten(card) {
						cards = append(cards, &card)
						c.writehash(card)
					}
				}

				count++
				stime = stime.Add(time.Hour * 24)
			}
		} else if entry.daily {
			// Hack central
			stime := ts
			stime = stime.Add(-time.Hour * time.Duration(stime.Hour()))
			stime = stime.Add(-time.Minute * time.Duration(stime.Minute()))
			stime = stime.Add(-time.Second * time.Duration(stime.Second()))
			stime = stime.Add(time.Hour * time.Duration(5))

			count := 1
			for stime.Before(te) {
				card := pb.Card{Text: entry.text, Action: pb.Card_DISMISS, ApplicationDate: stime.Unix(), Priority: -1, Hash: entry.hash}
				if !c.isWritten(card) {
					cards = append(cards, &card)
					c.writehash(card)
				}

				count++
				stime = stime.Add(time.Hour * 24)
			}
		}
	}
	c.crons = c.crons[newindex:]
	return cards
}

func (c *Cron) logd() {
	log.Printf("LEN = %v", len(c.crons))
}

func matches(s string, strs []string) bool {
	for _, str := range strs {
		if s == str {
			return true
		}
	}
	return false
}

func (c *Cron) loadline(line string) {
	elems := strings.Split(line, "~")
	entry := cronentry{}
	if matches(elems[0], daysOfTheWeek) {
		entry.day = elems[0]
	} else if elems[0] == "Daily" {
		entry.daily = true
	} else {
		t, _ := getTime(elems[0])
		entry.time = &t
	}
	entry.text = elems[2] + "|" + elems[3]
	entry.hash = elems[1] + "-" + elems[4]

	c.crons = append(c.crons, entry)
}
<|endoftext|>