<commit_before>package sudoku

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
)

type Availables struct {
	Size    int
	Numbers map[int]bool
	Length  int
}

type Sudoku struct {
	Debug      bool
	Size       int
	SquareSize int
	Grid       [][]int
	Availables [][]Availables
	Groups     Groups
	BruteLimit int

	DoResolveNumbersThatAreOnlyInOnePosition    bool
	DoResolveNumbersThatCanOnlyBeInFewPositions bool
	DoResolveOnlyOne                            bool
}

type GroupType int

const (
	HorizontalGroup GroupType = iota
	VerticalGroup
	RegionGroup
)

type Groups map[GroupType][]Group

type Position struct {
	Y int
	X int
}

type Group struct {
	Size      int
	Positions []Position
}

func NewSudoku() Sudoku {
	return NewSudokuWithSize(3)
}

func (s *Sudoku) Clone(dest *Sudoku) {
	dest.Debug = s.Debug
	dest.Size = s.Size
	dest.SquareSize = s.SquareSize
	dest.BruteLimit = s.BruteLimit

	dest.DoResolveNumbersThatAreOnlyInOnePosition = s.DoResolveNumbersThatAreOnlyInOnePosition
	dest.DoResolveNumbersThatCanOnlyBeInFewPositions = s.DoResolveNumbersThatCanOnlyBeInFewPositions
	dest.DoResolveOnlyOne = s.DoResolveOnlyOne

	dest.initFields()
	dest.Groups = s.Groups

	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] > 0 {
				dest.SetNumber(y, x, s.Grid[y][x])
			}
		}
	}
}

func (s *Sudoku) initFields() {
	s.Grid = make([][]int, s.Size)
	s.Availables = make([][]Availables, s.Size)
	s.Groups = make(map[GroupType][]Group, 0)
	for i := 0; i < s.Size; i++ {
		s.Grid[i] = make([]int, s.Size)
		s.Availables[i] = make([]Availables, s.Size)
		for j := 0; j < s.Size; j++ {
			s.Availables[i][j] = NewAvailables(s.Size)
		}
	}
}

func NewSudokuWithSize(sqsize int) Sudoku {
	size := sqsize * sqsize
	sudoku := Sudoku{
		Debug:      os.Getenv("DEBUG") == "1",
		SquareSize: sqsize,
		Size:       size,
		BruteLimit: 2,

		DoResolveNumbersThatAreOnlyInOnePosition:    true,
		DoResolveNumbersThatCanOnlyBeInFewPositions: false,
		DoResolveOnlyOne:                            true,
	}
	sudoku.initFields()
	sudoku.initGroups()
	return sudoku
}

func (s *Sudoku) initGroups() {
	// horizontal groups
	for y := 0; y < s.Size; y++ {
		group := Group{}
		for x := 0; x < s.Size; x++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		s.Groups[HorizontalGroup] = append(s.Groups[HorizontalGroup], group)
	}

	// vertical groups
	for x := 0; x < s.Size; x++ {
		group := Group{}
		for y := 0; y < s.Size; y++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		s.Groups[VerticalGroup] = append(s.Groups[VerticalGroup], group)
	}
	// zone groups
	for a := 0; a < s.SquareSize; a++ {
		for b := 0; b < s.SquareSize; b++ {
			group := Group{}
			for c := 0; c < s.SquareSize; c++ {
				for d := 0; d < s.SquareSize; d++ {
					y := a*s.SquareSize + c
					x := b*s.SquareSize + d
					group.Positions = append(group.Positions, Position{y, x})
				}
			}
			s.Groups[RegionGroup] = append(s.Groups[RegionGroup], group)
		}
	}
}

func NewAvailables(size int) Availables {
	availables := Availables{
		Size:    size,
		Numbers: make(map[int]bool, size),
		Length:  size,
	}
	for i := 1; i <= size; i++ {
		availables.Numbers[i] = true
	}
	return availables
}

func (a *Availables) String() string {
	output := ""
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			output += strconv.Itoa(i)
		} else {
			output += "."
		}
	}
	return output
}

func (a *Availables) SetNumber(number int) {
	for i := 1; i <= a.Size; i++ {
		a.Numbers[i] = i == number
	}
	a.Length = 1
}

func (a *Availables) Availables() []int {
	availables := []int{}
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			availables = append(availables, i)
		}
	}
	return availables
}

func (a *Availables) RemoveNumber(number int) bool {
	if a.Numbers[number] {
		a.Length--
		a.Numbers[number] = false
		return true
	}
	return false
}

func (g *Groups) AllGroups() []Group {
	groups := []Group{}
	for _, typeGroups := range *g {
		groups = append(groups, typeGroups...)
	}
	return groups
}

func (g *Groups) MatchCoords(y, x int) []Group {
	groups := []Group{}
	for _, group := range g.AllGroups() {
		for _, pos := range group.Positions {
			if pos.Y == y && pos.X == x {
				groups = append(groups, group)
				break
			}
		}
	}
	return groups
}

func (s *Sudoku) SetNumber(y, x, number int) {
	s.Grid[y][x] = number
	s.Availables[y][x].SetNumber(number)
	for _, group := range s.Groups.MatchCoords(y, x) {
		for _, pos := range group.Positions {
			s.Availables[pos.Y][pos.X].RemoveNumber(number)
		}
	}
}

func (s *Sudoku) ParseString(input string) error {
	input = strings.TrimSpace(input)
	lines := strings.Split(input, "\n")
	for y, line := range lines[1 : s.Size+1] {
		for x := 0; x < s.Size; x++ {
			col := line[1+x*2 : 1+x*2+1]
			if col != " " {
				colNb, err := strconv.Atoi(col)
				if err != nil {
					return err
				}
				s.SetNumber(y, x, colNb)
			}
		}
	}
	return nil
}

func (s *Sudoku) String() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))

	for _, gridLine := range s.Grid {
		line := []string{}
		for _, col := range gridLine {
			line = append(line, strconv.Itoa(col))
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lineStr = strings.Replace(lineStr, "0", " ", -1)
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) AvailablesString() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	for _, availablesLine := range s.Availables {
		line := []string{}
		for _, availables := range availablesLine {
			line = append(line, availables.String())
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) Missings() int {
	missings := 0
	for _, line := range s.Grid {
		for _, col := range line {
			if col == 0 {
				missings++
			}
		}
	}
	return missings
}

func (s *Sudoku) ResolveOnlyOne() int {
	changed := 0
	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] != 0 {
				continue
			}
			if s.Availables[y][x].Length == 1 {
				s.SetNumber(y, x, s.Availables[y][x].Availables()[0])
				changed++
			}
		}
	}
	return changed
}

func (s *Sudoku) RemoveNumbersThatCanOnlyBeInFewPositions() int {
	changes := 0
	for _, group := range s.Groups.AllGroups() {
		for idxA, posA := range group.Positions {
			identicalSlots := 0
			availableA := s.Availables[posA.Y][posA.X]
			for idxB, posB := range group.Positions {
				if idxA == idxB {
					continue
				}
				availableB := s.Availables[posB.Y][posB.X]
				if availableA.String() == availableB.String() {
					identicalSlots++
				}
			}
			if identicalSlots != availableA.Length-1 {
				continue
			}
			for idxB, posB := range group.Positions {
				if idxA == idxB {
					continue
				}
				availableB := s.Availables[posB.Y][posB.X]
				if availableA.String() != availableB.String() {
					for _, number := range availableA.Availables() {
						if s.Availables[posB.Y][posB.X].RemoveNumber(number) {
							changes++
						}
					}
				}
			}
		}
	}
	return changes
}

func (s *Sudoku) ResolveNumbersThatAreOnlyInOnePosition() int {
	changes := 0
	for _, group := range s.Groups.AllGroups() {
		for number := 1; number <= s.Size; number++ {
			count := 0
			for _, pos := range group.Positions {
				if s.Availables[pos.Y][pos.X].Numbers[number] {
					count++
				}
			}
			if count == 1 {
				for _, pos := range group.Positions {
					if s.Availables[pos.Y][pos.X].Numbers[number] {
						s.SetNumber(pos.Y, pos.X, number)
						changes++
					}
				}
			}
		}
	}
	return changes
}

func (s *Sudoku) ResolveRec(depth int) (*Sudoku, error) {
	changes := 0
	iteration := 0
	kind := "start"

start:
	if s.Debug {
		logrus.Infof("#######  depth=%-2d iteration=%-3d changes=%-2d missings=%d kind=%s\n%s\n%s", depth, iteration, changes, s.Missings(), kind, s.String(), s.AvailablesString())
		logrus.Infof(strings.Repeat("#", 42))
	}
	iteration++

	if s.DoResolveOnlyOne {
		kind = "resolve-only-one"
		if changes = s.ResolveOnlyOne(); changes > 0 {
			goto start
		}
	}

	if s.DoResolveNumbersThatAreOnlyInOnePosition {
		kind = "resolve-numbers-that-are-only-in-one-position"
		if changes = s.ResolveNumbersThatAreOnlyInOnePosition(); changes > 0 {
			goto start
		}
	}

	if s.DoResolveNumbersThatCanOnlyBeInFewPositions && depth == 0 {
		kind = "resolve-numbers-that-can-only-be-in-few-positions"
		if changes = s.RemoveNumbersThatCanOnlyBeInFewPositions(); changes > 0 {
			goto start
		}
	}

	// Brute force
	if s.Missings() == 0 {
		return s, nil
	}

	if depth >= s.BruteLimit {
		return s, fmt.Errorf("Too deep")
	}

	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Availables[y][x].Length > 0 {
				clone := Sudoku{}
				s.Clone(&clone)
				clone.SetNumber(y, x, s.Availables[y][x].Availables()[0])
				newSudoku, err := clone.ResolveRec(depth + 1)
				if err != nil {
					continue
				}
				if newSudoku.Missings() == 0 {
					return newSudoku, nil
				}
			}
		}
	}

	return s, nil
}

func (s *Sudoku) Resolve() error {
	newSudoku, err := s.ResolveRec(0)
	if err != nil {
		return err
	}

	if newSudoku != s {
		newSudoku.Clone(s)
	}
	return nil
}
<commit_msg>Splitted up resolver in two sections (brute/non-brute)<commit_after>package sudoku

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
)

type Availables struct {
	Size    int
	Numbers map[int]bool
	Length  int
}

type Sudoku struct {
	Debug      bool
	Size       int
	SquareSize int
	Grid       [][]int
	Availables [][]Availables
	Groups     Groups
	BruteLimit int

	DoResolveNumbersThatAreOnlyInOnePosition    bool
	DoResolveNumbersThatCanOnlyBeInFewPositions bool
	DoResolveOnlyOne                            bool
}

type GroupType int

const (
	HorizontalGroup GroupType = iota
	VerticalGroup
	RegionGroup
)

type Groups map[GroupType][]Group

type Position struct {
	Y int
	X int
}

type Group struct {
	Size      int
	Positions []Position
}

func NewSudoku() Sudoku {
	return NewSudokuWithSize(3)
}

func (s *Sudoku) Clone(dest *Sudoku) {
	dest.Debug = s.Debug
	dest.Size = s.Size
	dest.SquareSize = s.SquareSize
	dest.BruteLimit = s.BruteLimit

	dest.DoResolveNumbersThatAreOnlyInOnePosition = s.DoResolveNumbersThatAreOnlyInOnePosition
	dest.DoResolveNumbersThatCanOnlyBeInFewPositions = s.DoResolveNumbersThatCanOnlyBeInFewPositions
	dest.DoResolveOnlyOne = s.DoResolveOnlyOne

	dest.initFields()
	dest.Groups = s.Groups

	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] > 0 {
				dest.SetNumber(y, x, s.Grid[y][x])
			}
		}
	}
}

func (s *Sudoku) initFields() {
	s.Grid = make([][]int, s.Size)
	s.Availables = make([][]Availables, s.Size)
	s.Groups = make(map[GroupType][]Group, 0)
	for i := 0; i < s.Size; i++ {
		s.Grid[i] = make([]int, s.Size)
		s.Availables[i] = make([]Availables, s.Size)
		for j := 0; j < s.Size; j++ {
			s.Availables[i][j] = NewAvailables(s.Size)
		}
	}
}

func NewSudokuWithSize(sqsize int) Sudoku {
	size := sqsize * sqsize
	sudoku := Sudoku{
		Debug:      os.Getenv("DEBUG") == "1",
		SquareSize: sqsize,
		Size:       size,
		BruteLimit: 2,

		DoResolveNumbersThatAreOnlyInOnePosition:    true,
		DoResolveNumbersThatCanOnlyBeInFewPositions: false,
		DoResolveOnlyOne:                            true,
	}
	sudoku.initFields()
	sudoku.initGroups()
	return sudoku
}

func (s *Sudoku) initGroups() {
	// horizontal groups
	for y := 0; y < s.Size; y++ {
		group := Group{}
		for x := 0; x < s.Size; x++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		s.Groups[HorizontalGroup] = append(s.Groups[HorizontalGroup], group)
	}

	// vertical groups
	for x := 0; x < s.Size; x++ {
		group := Group{}
		for y := 0; y < s.Size; y++ {
			group.Positions = append(group.Positions, Position{y, x})
		}
		s.Groups[VerticalGroup] = append(s.Groups[VerticalGroup], group)
	}
	// zone groups
	for a := 0; a < s.SquareSize; a++ {
		for b := 0; b < s.SquareSize; b++ {
			group := Group{}
			for c := 0; c < s.SquareSize; c++ {
				for d := 0; d < s.SquareSize; d++ {
					y := a*s.SquareSize + c
					x := b*s.SquareSize + d
					group.Positions = append(group.Positions, Position{y, x})
				}
			}
			s.Groups[RegionGroup] = append(s.Groups[RegionGroup], group)
		}
	}
}

func NewAvailables(size int) Availables {
	availables := Availables{
		Size:    size,
		Numbers: make(map[int]bool, size),
		Length:  size,
	}
	for i := 1; i <= size; i++ {
		availables.Numbers[i] = true
	}
	return availables
}

func (a *Availables) String() string {
	output := ""
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			output += strconv.Itoa(i)
		} else {
			output += "."
		}
	}
	return output
}

func (a *Availables) SetNumber(number int) {
	for i := 1; i <= a.Size; i++ {
		a.Numbers[i] = i == number
	}
	a.Length = 1
}

func (a *Availables) Availables() []int {
	availables := []int{}
	for i := 1; i <= a.Size; i++ {
		if a.Numbers[i] {
			availables = append(availables, i)
		}
	}
	return availables
}

func (a *Availables) RemoveNumber(number int) bool {
	if a.Numbers[number] {
		a.Length--
		a.Numbers[number] = false
		return true
	}
	return false
}

func (g *Groups) AllGroups() []Group {
	groups := []Group{}
	for _, typeGroups := range *g {
		groups = append(groups, typeGroups...)
	}
	return groups
}

func (g *Groups) MatchCoords(y, x int) []Group {
	groups := []Group{}
	for _, group := range g.AllGroups() {
		for _, pos := range group.Positions {
			if pos.Y == y && pos.X == x {
				groups = append(groups, group)
				break
			}
		}
	}
	return groups
}

func (s *Sudoku) SetNumber(y, x, number int) {
	s.Grid[y][x] = number
	s.Availables[y][x].SetNumber(number)
	for _, group := range s.Groups.MatchCoords(y, x) {
		for _, pos := range group.Positions {
			s.Availables[pos.Y][pos.X].RemoveNumber(number)
		}
	}
}

func (s *Sudoku) ParseString(input string) error {
	input = strings.TrimSpace(input)
	lines := strings.Split(input, "\n")
	for y, line := range lines[1 : s.Size+1] {
		for x := 0; x < s.Size; x++ {
			col := line[1+x*2 : 1+x*2+1]
			if col != " " {
				colNb, err := strconv.Atoi(col)
				if err != nil {
					return err
				}
				s.SetNumber(y, x, colNb)
			}
		}
	}
	return nil
}

func (s *Sudoku) String() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))

	for _, gridLine := range s.Grid {
		line := []string{}
		for _, col := range gridLine {
			line = append(line, strconv.Itoa(col))
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lineStr = strings.Replace(lineStr, "0", " ", -1)
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*2-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) AvailablesString() string {
	lines := []string{}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	for _, availablesLine := range s.Availables {
		line := []string{}
		for _, availables := range availablesLine {
			line = append(line, availables.String())
		}
		lineStr := fmt.Sprintf("|%s|", strings.Join(line, " "))
		lines = append(lines, lineStr)
	}
	lines = append(lines, fmt.Sprintf("+%s+", strings.Repeat("-", s.Size*(s.Size+1)-1)))
	return strings.Join(lines, "\n")
}

func (s *Sudoku) Missings() int {
	missings := 0
	for _, line := range s.Grid {
		for _, col := range line {
			if col == 0 {
				missings++
			}
		}
	}
	return missings
}

func (s *Sudoku) ResolveOnlyOne() int {
	changed := 0
	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Grid[y][x] != 0 {
				continue
			}
			if s.Availables[y][x].Length == 1 {
				s.SetNumber(y, x, s.Availables[y][x].Availables()[0])
				changed++
			}
		}
	}
	return changed
}

func (s *Sudoku) RemoveNumbersThatCanOnlyBeInFewPositions() int {
	changes := 0
	for _, group := range s.Groups.AllGroups() {
		for idxA, posA := range group.Positions {
			identicalSlots := 0
			availableA := s.Availables[posA.Y][posA.X]
			for idxB, posB := range group.Positions {
				if idxA == idxB {
					continue
				}
				availableB := s.Availables[posB.Y][posB.X]
				if availableA.String() == availableB.String() {
					identicalSlots++
				}
			}
			if identicalSlots != availableA.Length-1 {
				continue
			}
			for idxB, posB := range group.Positions {
				if idxA == idxB {
					continue
				}
				availableB := s.Availables[posB.Y][posB.X]
				if availableA.String() != availableB.String() {
					for _, number := range availableA.Availables() {
						if s.Availables[posB.Y][posB.X].RemoveNumber(number) {
							changes++
						}
					}
				}
			}
		}
	}
	return changes
}

func (s *Sudoku) ResolveNumbersThatAreOnlyInOnePosition() int {
	changes := 0
	for _, group := range s.Groups.AllGroups() {
		for number := 1; number <= s.Size; number++ {
			count := 0
			for _, pos := range group.Positions {
				if s.Availables[pos.Y][pos.X].Numbers[number] {
					count++
				}
			}
			if count == 1 {
				for _, pos := range group.Positions {
					if s.Availables[pos.Y][pos.X].Numbers[number] {
						s.SetNumber(pos.Y, pos.X, number)
						changes++
					}
				}
			}
		}
	}
	return changes
}

func (s *Sudoku) ResolveNonBrute(depth int) {
	changes := 0
	iteration := 0
	kind := "start"

start:
	if s.Missings() == 0 {
		return
	}
	if s.Debug {
		logrus.Infof("#######  depth=%-2d iteration=%-3d changes=%-2d missings=%d kind=%s\n%s\n%s", depth, iteration, changes, s.Missings(), kind, s.String(), s.AvailablesString())
		logrus.Infof(strings.Repeat("#", 42))
	}
	iteration++

	if s.DoResolveOnlyOne {
		kind = "resolve-only-one"
		if changes = s.ResolveOnlyOne(); changes > 0 {
			goto start
		}
	}

	if s.DoResolveNumbersThatAreOnlyInOnePosition {
		kind = "resolve-numbers-that-are-only-in-one-position"
		if changes = s.ResolveNumbersThatAreOnlyInOnePosition(); changes > 0 {
			goto start
		}
	}

	if s.DoResolveNumbersThatCanOnlyBeInFewPositions && depth == 0 {
		kind = "resolve-numbers-that-can-only-be-in-few-positions"
		if changes = s.RemoveNumbersThatCanOnlyBeInFewPositions(); changes > 0 {
			goto start
		}
	}
}

func (s *Sudoku) ResolveRec(depth int) (*Sudoku, error) {
	s.ResolveNonBrute(depth)
	if s.Missings() == 0 {
		return s, nil
	}

	if depth >= s.BruteLimit {
		return s, fmt.Errorf("Too deep")
	}

	for y := 0; y < s.Size; y++ {
		for x := 0; x < s.Size; x++ {
			if s.Availables[y][x].Length > 0 {
				clone := Sudoku{}
				s.Clone(&clone)
				clone.SetNumber(y, x, s.Availables[y][x].Availables()[0])
				newSudoku, err := clone.ResolveRec(depth + 1)
				if err != nil {
					continue
				}
				if newSudoku.Missings() == 0 {
					return newSudoku, nil
				}
			}
		}
	}

	return s, nil
}

func (s *Sudoku) Resolve() error {
	newSudoku, err := s.ResolveRec(0)
	if err != nil {
		return err
	}

	if newSudoku != s {
		newSudoku.Clone(s)
	}
	return nil
}
<|endoftext|>


<commit_before>package survey

import (
	"errors"
	"fmt"

	"github.com/AlecAivazis/survey/core"
)

// Validator is a function passed to a Question in order to redefine
type Validator func(interface{}) error

// Question is the core data structure for a survey questionnaire.
type Question struct {
	Name     string
	Prompt   Prompt
	Validate Validator
}

// Prompt is the primary interface for the objects that can take user input
// and return a string value.
type Prompt interface {
	Prompt() (interface{}, error)
	Cleanup(interface{}) error
}

var ErrorTemplate = `{{color "red"}}{{ ErrorIcon }} Sorry, your reply was invalid: {{.Error}}{{color "reset"}}
`

// AskOne asks a single question without performing validation on the answer.
func AskOne(p Prompt, t interface{}, v Validator) error {
	err := Ask([]*Question{{Prompt: p, Validate: v}}, t)
	if err != nil {
		return err
	}

	return nil
}

// Ask performs the prompt loop
func Ask(qs []*Question, t interface{}) error {

	// if we weren't passed a place to record the answers
	if t == nil {
		// we can't go any further
		return errors.New("cannot call Ask() with a nil reference to record the answers")
	}

	// go over every question
	for _, q := range qs {
		// grab the user input and save it
		ans, err := q.Prompt.Prompt()
		// if there was a problem
		if err != nil {
			return err
		}

		// if there is a validate handler for this question
		if q.Validate != nil {
			// wait for a valid response
			for invalid := q.Validate(ans); invalid != nil; invalid = q.Validate(ans) {
				out, err := core.RunTemplate(ErrorTemplate, invalid)
				if err != nil {
					return err
				}
				// send the message to the user
				fmt.Print(out)
				// ask for more input
				ans, err = q.Prompt.Prompt()
				// if there was a problem
				if err != nil {
					return err
				}
			}
		}

		// tell the prompt to cleanup with the validated value
		q.Prompt.Cleanup(ans)

		// if something went wrong
		if err != nil {
			// stop listening
			return err
		}

		// add it to the map
		err = core.WriteAnswer(t, q.Name, ans)
		// if something went wrong
		if err != nil {
			return err
		}

	}
	// return the response
	return nil
}
<commit_msg>fix color output on windows<commit_after>package survey

import (
	"errors"

	"github.com/AlecAivazis/survey/core"
	"github.com/AlecAivazis/survey/terminal"
)

// Validator is a function passed to a Question in order to redefine
type Validator func(interface{}) error

// Question is the core data structure for a survey questionnaire.
type Question struct {
	Name     string
	Prompt   Prompt
	Validate Validator
}

// Prompt is the primary interface for the objects that can take user input
// and return a string value.
type Prompt interface {
	Prompt() (interface{}, error)
	Cleanup(interface{}) error
}

var ErrorTemplate = `{{color "red"}}{{ ErrorIcon }} Sorry, your reply was invalid: {{.Error}}{{color "reset"}}
`

// AskOne asks a single question without performing validation on the answer.
func AskOne(p Prompt, t interface{}, v Validator) error {
	err := Ask([]*Question{{Prompt: p, Validate: v}}, t)
	if err != nil {
		return err
	}

	return nil
}

// Ask performs the prompt loop
func Ask(qs []*Question, t interface{}) error {

	// if we weren't passed a place to record the answers
	if t == nil {
		// we can't go any further
		return errors.New("cannot call Ask() with a nil reference to record the answers")
	}

	// go over every question
	for _, q := range qs {
		// grab the user input and save it
		ans, err := q.Prompt.Prompt()
		// if there was a problem
		if err != nil {
			return err
		}

		// if there is a validate handler for this question
		if q.Validate != nil {
			// wait for a valid response
			for invalid := q.Validate(ans); invalid != nil; invalid = q.Validate(ans) {
				out, err := core.RunTemplate(ErrorTemplate, invalid)
				if err != nil {
					return err
				}
				// send the message to the user
				terminal.Print(out)
				// ask for more input
				ans, err = q.Prompt.Prompt()
				// if there was a problem
				if err != nil {
					return err
				}
			}
		}

		// tell the prompt to cleanup with the validated value
		q.Prompt.Cleanup(ans)

		// if something went wrong
		if err != nil {
			// stop listening
			return err
		}

		// add it to the map
		err = core.WriteAnswer(t, q.Name, ans)
		// if something went wrong
		if err != nil {
			return err
		}

	}
	// return the response
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"
	"time"
)

type File struct {
	Filename string
	Path     string
	Size     int64
	ModTime  time.Time
	IsDir    bool
}

type FileMessage struct {
	ConnId   int                    `json:"id"`
	Function string                 `json:"type"`
	Data     map[string]interface{} `json:"data"`
}

func FileStruct(file os.FileInfo, dir string) File {
	return File{
		Filename: file.Name(),
		Path:     path.Join(dir, file.Name()),
		ModTime:  file.ModTime(),
		IsDir:    file.Isdir(),
		Size:     file.Size(),
	}
}

// Echos the $HOME env to find the home directory, returns root if err
func getHomeDir(id int) string {
	// Create a new ssh session to access the remote host's $HOME env variable
	session, err := conns[id].sshClient.NewSession()
	defer session.Close()

	if err != nil {
		fmt.Println("Error creating ssh session:", err)
		return "/"
	}
	output, err := session.Output("echo $HOME")
	if err != nil {
		fmt.Println("Error finding home dir:", err)
		return "/"
	}
	return strings.TrimSpace(string(output))
}

func printFile(id int, filename, filepath string) {
	dest, err := os.Create(filename)
	if err != nil {
		fmt.Println(err)
	}

	src, err := conns[id].sftpClient.Open(path.Join(filepath, filename))
	info, err := src.Stat()
	contents := make([]byte, info.Size())

	src.Read(contents)
	dest.Write(contents)
	dest.Close()
}

// Print the target directory's file listing
func printDirectory(id int, dirpath string) {
	fmt.Println("Printing contents of", dirpath)

	listing, err := conns[id].sftpClient.ReadDir(dirpath)
	if err != nil {
		log.Fatal("Could not open directory : ", err)
	}

	files := make([]File, len(listing))
	for i, file := range listing {
		files[i] = FileStruct(file, dirpath)
	}

	data := make(map[string]interface{})
	data["files"] = files
	data["path"] = dirpath

	jsonMessage, _ := json.Marshal(FileMessage{id, "FETCH_FILES_SUCCESS", data})
	_, _ = socket.Write(jsonMessage)
}

// MoveDirectory moves named directory from src -> dest
func MoveDirectory(from, to string, src, dest int) {
	target := path.Join(to, path.Base(from))
	session, err := conns[dest].sshClient.NewSession()
	if err != nil {
		log.Fatal(err)
	}
	session.Run("sh -c `mkdir " + target + " && cd " + target + "`")
	session.Close()

	// Read contents of the directory
	files, err := conns[src].sftpClient.ReadDir(from)
	if err != nil {
		log.Println(err)
		session, err = conns[src].sshClient.NewSession()
		if err != nil {
			log.Fatal(err)
		}
		session.Run("cd ..")
		session.Close()
		return
	}

	// Copy each file in that directory
	for _, file := range files {
		destination := path.Join(from, file.Name())
		if file.IsDir() {
			MoveDirectory(destination, target, src, dest)
		} else {
			MoveFile(destination, target, src, dest)
		}
	}

	// Change back to parent directory
	session, err = conns[src].sshClient.NewSession()
	if err != nil {
		log.Fatal(err)
	}
	session.Run("cd ..")
	session.Close()
}

/// MoveFile moves named file from src -> dest
func MoveFile(from, to string, src, dest int) {
	file, err := conns[src].sftpClient.Open(from)
	if err != nil {
		log.Fatal(err)
	}

	contents, err := ioutil.ReadAll(file)
	file.Close()

	file, err = conns[dest].sftpClient.Create(path.Join(to, path.Base(from)))
	if err != nil {
		log.Fatal(err)
	}
	file.Write(contents)
	file.Close()
}
<commit_msg>Fixed your typo for you Alex 😂<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"
	"time"
)

type File struct {
	Filename string
	Path     string
	Size     int64
	ModTime  time.Time
	IsDir    bool
}

type FileMessage struct {
	ConnId   int                    `json:"id"`
	Function string                 `json:"type"`
	Data     map[string]interface{} `json:"data"`
}

func FileStruct(file os.FileInfo, dir string) File {
	return File{
		Filename: file.Name(),
		Path:     path.Join(dir, file.Name()),
		ModTime:  file.ModTime(),
		IsDir:    file.IsDir(),
		Size:     file.Size(),
	}
}

// Echos the $HOME env to find the home directory, returns root if err
func getHomeDir(id int) string {
	// Create a new ssh session to access the remote host's $HOME env variable
	session, err := conns[id].sshClient.NewSession()
	defer session.Close()

	if err != nil {
		fmt.Println("Error creating ssh session:", err)
		return "/"
	}
	output, err := session.Output("echo $HOME")
	if err != nil {
		fmt.Println("Error finding home dir:", err)
		return "/"
	}
	return strings.TrimSpace(string(output))
}

func printFile(id int, filename, filepath string) {
	dest, err := os.Create(filename)
	if err != nil {
		fmt.Println(err)
	}

	src, err := conns[id].sftpClient.Open(path.Join(filepath, filename))
	info, err := src.Stat()
	contents := make([]byte, info.Size())

	src.Read(contents)
	dest.Write(contents)
	dest.Close()
}

// Print the target directory's file listing
func printDirectory(id int, dirpath string) {
	fmt.Println("Printing contents of", dirpath)

	listing, err := conns[id].sftpClient.ReadDir(dirpath)
	if err != nil {
		log.Fatal("Could not open directory : ", err)
	}

	files := make([]File, len(listing))
	for i, file := range listing {
		files[i] = FileStruct(file, dirpath)
	}

	data := make(map[string]interface{})
	data["files"] = files
	data["path"] = dirpath

	jsonMessage, _ := json.Marshal(FileMessage{id, "FETCH_FILES_SUCCESS", data})
	_, _ = socket.Write(jsonMessage)
}

// MoveDirectory moves named directory from src -> dest
func MoveDirectory(from, to string, src, dest int) {
	target := path.Join(to, path.Base(from))
	session, err := conns[dest].sshClient.NewSession()
	if err != nil {
		log.Fatal(err)
	}
	session.Run("sh -c `mkdir " + target + " && cd " + target + "`")
	session.Close()

	// Read contents of the directory
	files, err := conns[src].sftpClient.ReadDir(from)
	if err != nil {
		log.Println(err)
		session, err = conns[src].sshClient.NewSession()
		if err != nil {
			log.Fatal(err)
		}
		session.Run("cd ..")
		session.Close()
		return
	}

	// Copy each file in that directory
	for _, file := range files {
		destination := path.Join(from, file.Name())
		if file.IsDir() {
			MoveDirectory(destination, target, src, dest)
		} else {
			MoveFile(destination, target, src, dest)
		}
	}

	// Change back to parent directory
	session, err = conns[src].sshClient.NewSession()
	if err != nil {
		log.Fatal(err)
	}
	session.Run("cd ..")
	session.Close()
}

/// MoveFile moves named file from src -> dest
func MoveFile(from, to string, src, dest int) {
	file, err := conns[src].sftpClient.Open(from)
	if err != nil {
		log.Fatal(err)
	}

	contents, err := ioutil.ReadAll(file)
	file.Close()

	file, err = conns[dest].sftpClient.Create(path.Join(to, path.Base(from)))
	if err != nil {
		log.Fatal(err)
	}
	file.Write(contents)
	file.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/kennygrant/sanitize"
)

const (
	ignoreTranslations = true
	downloadPath       = "./downloads/"
)

var (
	preferredMimeTypes    = []string{"video/webm", "video/mp4", "video/ogg", "audio/ogg", "audio/opus", "audio/mpeg", "application/x-subrip"}
	extensionForMimeTypes = make(map[string]string)
)

type Conference struct {
	Acronym        string `json:"acronym"`
	AspectRatio    string `json:"aspect_ratio"`
	ImagesURL      string `json:"images_url"`
	LogoURL        string `json:"logo_url"`
	RecordingsURL  string `json:"recordings_url"`
	ScheduleURL    string `json:"schedule_url"`
	Slug           string `json:"slug"`
	Title          string `json:"title"`
	UpdatedAt      string `json:"updated_at"`
	URL            string `json:"url"`
	WebgenLocation string `json:"webgen_location"`
}

type Conferences struct {
	Conferences []Conference `json:"conferences"`
}

func priorityForMimeType(mime string) int {
	for i, v := range preferredMimeTypes {
		if strings.ToLower(mime) == v {
			return i
		}
	}

	return -1
}

func findConferences(url string) (Conferences, error) {
	ci := Conferences{}

	r, err := http.Get(url)
	if err != nil {
		return ci, err
	}
	defer r.Body.Close()

	err = json.NewDecoder(r.Body).Decode(&ci)
	return ci, err
}

func main() {
	extensionForMimeTypes["video/webm"] = "webm"
	extensionForMimeTypes["video/mp4"] = "mp4"
	extensionForMimeTypes["video/ogg"] = "ogm"
	extensionForMimeTypes["audio/ogg"] = "ogg"
	extensionForMimeTypes["audio/opus"] = "opus"
	extensionForMimeTypes["audio/mpeg"] = "mp3"

	ci, err := findConferences("https://api.media.ccc.de/public/conferences")
	if err != nil {
		panic(err)
	}

	for _, v := range ci.Conferences {
		fmt.Printf("Found conference: %s, URL: %s\n", v.Title, v.URL)

		events, err := findEvents(v.URL)
		if err != nil {
			panic(err)
		}

		for _, e := range events.Events {
			desc := strings.Replace(sanitize.HTML(e.Description), "\n", "", -1)
			if len(desc) > 48 {
				desc = desc[:45] + "..."
			}
			if len(desc) > 0 {
				desc = " - " + desc
			}
			fmt.Printf("\tFound event: %s%s\n", e.Title, desc)

			media, err := findMedia(e.URL)
			if err != nil {
				panic(err)
			}

			bestMatch := Recording{}
			highestPriority := -1
			if len(media.Recordings) == 0 {
				panic("No recordings found for this event!")
			}
			for _, m := range media.Recordings {
				if ignoreTranslations && m.Language != e.OriginalLanguage {
					continue
				}

				if m.Width == 0 {
					fmt.Printf("\t\tFound audio (%s): %d minutes (HD: %t, %dMiB) %s\n", m.MimeType, m.Length/60, m.HighQuality, m.Size, m.URL)
				} else {
					fmt.Printf("\t\tFound video (%s): %d minutes, %dx%d (HD: %t, %dMiB) %s\n", m.MimeType, m.Length/60, m.Width, m.Height, m.HighQuality, m.Size, m.URL)
				}

				prio := priorityForMimeType(m.MimeType)
				if prio < 0 {
					panic("Unknown mimetype encountered:" + m.MimeType)
				}

				if prio < highestPriority ||
					(prio == highestPriority && m.Width > bestMatch.Width) ||
					highestPriority == -1 {
					highestPriority = prio
					bestMatch = m
				}
			}

			err = download(v, e, bestMatch)
			if err != nil {
				panic(err)
			}

			fmt.Println()
		}
	}

	fmt.Println("Done.")
}
<commit_msg>Less indentation<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/kennygrant/sanitize"
)

const (
	ignoreTranslations = true
	downloadPath       = "./downloads/"
)

var (
	preferredMimeTypes    = []string{"video/webm", "video/mp4", "video/ogg", "audio/ogg", "audio/opus", "audio/mpeg", "application/x-subrip"}
	extensionForMimeTypes = make(map[string]string)
)

type Conference struct {
	Acronym        string `json:"acronym"`
	AspectRatio    string `json:"aspect_ratio"`
	ImagesURL      string `json:"images_url"`
	LogoURL        string `json:"logo_url"`
	RecordingsURL  string `json:"recordings_url"`
	ScheduleURL    string `json:"schedule_url"`
	Slug           string `json:"slug"`
	Title          string `json:"title"`
	UpdatedAt      string `json:"updated_at"`
	URL            string `json:"url"`
	WebgenLocation string `json:"webgen_location"`
}

type Conferences struct {
	Conferences []Conference `json:"conferences"`
}

func priorityForMimeType(mime string) int {
	for i, v := range preferredMimeTypes {
		if strings.ToLower(mime) == v {
			return i
		}
	}

	return -1
}

func findConferences(url string) (Conferences, error) {
	ci := Conferences{}

	r, err := http.Get(url)
	if err != nil {
		return ci, err
	}
	defer r.Body.Close()

	err = json.NewDecoder(r.Body).Decode(&ci)
	return ci, err
}

func main() {
	extensionForMimeTypes["video/webm"] = "webm"
	extensionForMimeTypes["video/mp4"] = "mp4"
	extensionForMimeTypes["video/ogg"] = "ogm"
	extensionForMimeTypes["audio/ogg"] = "ogg"
	extensionForMimeTypes["audio/opus"] = "opus"
	extensionForMimeTypes["audio/mpeg"] = "mp3"

	ci, err := findConferences("https://api.media.ccc.de/public/conferences")
	if err != nil {
		panic(err)
	}

	for _, v := range ci.Conferences {
		fmt.Printf("Conference: %s, URL: %s\n", v.Title, v.URL)

		events, err := findEvents(v.URL)
		if err != nil {
			panic(err)
		}

		for _, e := range events.Events {
			desc := strings.Replace(sanitize.HTML(e.Description), "\n", "", -1)
			if len(desc) > 48 {
				desc = desc[:45] + "..."
			}
			if len(desc) > 0 {
				desc = " - " + desc
			}
			fmt.Printf("Event: %s%s\n", e.Title, desc)

			media, err := findMedia(e.URL)
			if err != nil {
				panic(err)
			}

			bestMatch := Recording{}
			highestPriority := -1
			if len(media.Recordings) == 0 {
				panic("No recordings found for this event!")
			}
			for _, m := range media.Recordings {
				if ignoreTranslations && m.Language != e.OriginalLanguage {
					continue
				}

				if m.Width == 0 {
					fmt.Printf("\tFound audio (%s): %d minutes (HD: %t, %dMiB) %s\n", m.MimeType, m.Length/60, m.HighQuality, m.Size, m.URL)
				} else {
					fmt.Printf("\tFound video (%s): %d minutes, %dx%d (HD: %t, %dMiB) %s\n", m.MimeType, m.Length/60, m.Width, m.Height, m.HighQuality, m.Size, m.URL)
				}

				prio := priorityForMimeType(m.MimeType)
				if prio < 0 {
					panic("Unknown mimetype encountered:" + m.MimeType)
				}

				if prio < highestPriority ||
					(prio == highestPriority && m.Width > bestMatch.Width) ||
					highestPriority == -1 {
					highestPriority = prio
					bestMatch = m
				}
			}

			err = download(v, e, bestMatch)
			if err != nil {
				panic(err)
			}

			fmt.Println()
		}
	}

	fmt.Println("Done.")
}
<|endoftext|>


<commit_before>package docker

import (
	"context"
	"encoding/json"
	"time"
)

// VolumeUsageData represents usage data from the docker system api
// More Info Here https://dockr.ly/2PNzQyO
type VolumeUsageData struct {

	// The number of containers referencing this volume. This field
	// is set to `-1` if the reference-count is not available.
	//
	// Required: true
	RefCount int64 `json:"RefCount"`

	// Amount of disk space used by the volume (in bytes). This information
	// is only available for volumes created with the `"local"` volume
	// driver. For volumes created with other volume drivers, this field
	// is set to `-1` ("not available")
	//
	// Required: true
	Size int64 `json:"Size"`
}

// ImageSummary represents data about what images are
// currently known to docker
// More Info Here https://dockr.ly/2PNzQyO
type ImageSummary struct {
	Containers  int64             "json:\"Containers\""
	Created     int64             "json:\"Created\""
	ID          string            "json:\"Id\""
	Labels      map[string]string "json:\"Labels\""
	ParentID    string            "json:\"ParentId\""
	RepoDigests []string          "json:\"RepoDigests\""
	RepoTags    []string          "json:\"RepoTags\""
	SharedSize  int64             "json:\"SharedSize\""
	Size        int64             "json:\"Size\""
	VirtualSize int64             "json:\"VirtualSize\""
}

// BuildCache Holds metadata about what the build cache holds
// More Info Here https://dockr.ly/2PNzQyO
type BuildCache struct {
	ID          string
	Parent      string
	Type        string
	Description string
	InUse       bool
	Shared      bool
	Size        int64
	CreatedAt   time.Time
	LastUsedAt  *time.Time
	UsageCount  int
}

// DiskUsage holds information about what docker is using disk space on.
// More Info Here https://dockr.ly/2PNzQyO
type DiskUsage struct {
	LayersSize  int64
	Images      []*ImageSummary
	Containers  []*APIContainers
	Volumes     []*Volume
	BuildCache  []*BuildCache
	BuilderSize int64 // deprecated
}

// DiskUsageOptions only contains a context for canceling.
type DiskUsageOptions struct {
	Context context.Context
}

// DiskUsage returns a *DiskUsage describing what docker is using disk on.
//
// More Info Here https://dockr.ly/2PNzQyO
func (c *Client) DiskUsage(opts DiskUsageOptions) (*DiskUsage, error) {
	path := "/system/df"
	resp, err := c.do("GET", path, doOptions{context: opts.Context})
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var du *DiskUsage
	if err := json.NewDecoder(resp.Body).Decode(&du); err != nil {
		return nil, err
	}
	return du, nil
}
<commit_msg>remove deprecated fields, changed the nameds of the json tags to use backtics to avoid escaping<commit_after>package docker

import (
	"context"
	"encoding/json"
	"time"
)

// VolumeUsageData represents usage data from the docker system api
// More Info Here https://dockr.ly/2PNzQyO
type VolumeUsageData struct {

	// The number of containers referencing this volume. This field
	// is set to `-1` if the reference-count is not available.
	//
	// Required: true
	RefCount int64 `json:"RefCount"`

	// Amount of disk space used by the volume (in bytes). This information
	// is only available for volumes created with the `"local"` volume
	// driver. For volumes created with other volume drivers, this field
	// is set to `-1` ("not available")
	//
	// Required: true
	Size int64 `json:"Size"`
}

// ImageSummary represents data about what images are
// currently known to docker
// More Info Here https://dockr.ly/2PNzQyO
type ImageSummary struct {
	Containers  int64             `json:"Containers"`
	Created     int64             `json:"Created"`
	ID          string            `json:"Id"`
	Labels      map[string]string `json:"Labels"`
	ParentID    string            `json:"ParentId"`
	RepoDigests []string          `json:"RepoDigests"`
	RepoTags    []string          `json:"RepoTags"`
	SharedSize  int64             `json:"SharedSize"`
	Size        int64             `json:"Size"`
	VirtualSize int64             `json:"VirtualSize"`
}

// BuildCache Holds metadata about what the build cache holds
// More Info Here https://dockr.ly/2PNzQyO
type BuildCache struct {
	ID          string
	Parent      string
	Type        string
	Description string
	InUse       bool
	Shared      bool
	Size        int64
	CreatedAt   time.Time
	LastUsedAt  *time.Time
	UsageCount  int
}

// DiskUsage holds information about what docker is using disk space on.
// More Info Here https://dockr.ly/2PNzQyO
type DiskUsage struct {
	LayersSize int64
	Images     []*ImageSummary
	Containers []*APIContainers
	Volumes    []*Volume
}

// DiskUsageOptions only contains a context for canceling.
type DiskUsageOptions struct {
	Context context.Context
}

// DiskUsage returns a *DiskUsage describing what docker is using disk on.
//
// More Info Here https://dockr.ly/2PNzQyO
func (c *Client) DiskUsage(opts DiskUsageOptions) (*DiskUsage, error) {
	path := "/system/df"
	resp, err := c.do("GET", path, doOptions{context: opts.Context})
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var du *DiskUsage
	if err := json.NewDecoder(resp.Body).Decode(&du); err != nil {
		return nil, err
	}
	return du, nil
}
<|endoftext|>


<commit_before>package GoHPACK

import (
	"github.com/ami-GS/GoHPACK/huffman"
)

type Header struct {
	Name, Value string
}

func (h Header) size() uint32 {
	return uint32(len(h.Name + h.Value))
}

type Table struct {
	head, tail       *RingTable
	currentEntrySize uint32
	currentEntryNum  uint32
	dynamicTableSize uint32
	Huffman          *huffman.Node
}

type RingTable struct {
	header    Header
	Next, Pre *RingTable
}

func NewTable() (t Table) {
	t.currentEntryNum = 0
	t.currentEntrySize = 0
	t.dynamicTableSize = 4096
	t.Huffman = &huffman.Node{nil, nil, -1} // temporally
	t.Huffman.CreateTree()
	return
}

func (t *Table) FindHeader(h Header) (match bool, index int) {
	//here should be optimized
	preName := ""
	for i, header := range STATIC_TABLE {
		if header == h {
			return true, i
		} else if header.Name == h.Name && index == 0 {
			preName = header.Name
			index = i
			match = false
		} else if index != 0 && preName != header.Name {
			return match, index
		}
	}

	ring := t.head
	for i := 0; i < int(t.currentEntryNum); i++ {
		if ring.header == h {
			return true, i + int(STATIC_TABLE_NUM)
		} else if ring.header.Name == h.Name && index == 0 {
			match = false
			index = i + int(STATIC_TABLE_NUM)
		}
		ring = ring.Next
	}
	if index > 0 {
		return match, index
	}
	return false, -1 // not found on any table
}

func (t *Table) GetHeader(index uint32) Header {
	if 0 < index && index < uint32(STATIC_TABLE_NUM) {
		return STATIC_TABLE[index]
	} else if uint32(STATIC_TABLE_NUM) <= index && index <= uint32(STATIC_TABLE_NUM+byte(t.currentEntryNum)) {
		// get from dynamic table
		ring := t.head
		for i := uint32(0); i < index-uint32(STATIC_TABLE_NUM); i++ {
			ring = ring.Next
		}
		return ring.header
	}

	panic("error")
}

func (t *Table) getFromDynamicTable(index uint32) Header {
	index -= uint32(STATIC_TABLE_NUM)
	ring := t.head
	for i := uint32(0); i < index; i++ {
		ring = ring.Next
	}
	return ring.header
}

var nilElem *RingTable

func (t *Table) delLast() {
	t.currentEntrySize -= t.tail.header.size()
	deleated := t.tail
	t.tail = deleated.Pre
	deleated = nilElem
	t.currentEntryNum--
}

func (t *Table) insertFirst(header Header) {
	//here should be refactored
	elem := RingTable{header, nil, nil}

	if t.currentEntryNum >= 1 {
		elem.Next = t.head
		t.head.Pre = &elem
	}
	t.head = &elem

	if t.currentEntryNum == 0 {
		t.tail = &elem
	}
	t.currentEntryNum++
	t.currentEntrySize += header.size()
}

func (t *Table) AddHeader(header Header) {
	for t.currentEntrySize+header.size() > t.dynamicTableSize {
		t.delLast()

func (t *Table) PackContent(content string, toHuffman bool) []byte {
	if toHuffman {
		encoded, length := t.Huffman.Encode(content)
		intRep := PackIntRepresentation(uint32(length), 7)
		intRep[0] |= 0x80
		return append(intRep, encoded...)
	}
	intRep := PackIntRepresentation(uint32(len(content)), 7)
	return append(intRep, []byte(content)...)
}

func (t *Table) SetDynamicTableSize(size uint32) {
	t.dynamicTableSize = size
func (t *Table) ParseFromByte(buf []byte) (content string, cursor uint32) {
	length, cursor := ParseIntRepresentation(buf, 7)

	if buf[0]&0x80 > 0 {
		content = t.Huffman.Decode(buf[cursor:], length)
	} else {
		content = string(buf[cursor : cursor+length])
	}
	cursor += length
	return
}

func (table *Table) ParseHeader(index uint32, buf []byte, isIndexed bool) (name, value string, cursor uint32) {
	if c := uint32(0); !isIndexed {
		if index == 0 {
			name, c = table.ParseFromByte(buf[cursor:])
			cursor += c
		}
		value, c = table.ParseFromByte(buf[cursor:])
		cursor += c
	}

	if index > 0 {
		header := table.GetHeader(index)
		name = header.Name
		if len(value) == 0 {
			value = header.Value
		}
	}
	return
}

var STATIC_TABLE = &[...]Header{
	{"", ""},
	{":authority", ""},
	{":method", "GET"},
	{":method", "POST"},
	{":path", "/"},
	{":path", "/index.html"},
	{":scheme", "http"},
	{":scheme", "https"},
	{":status", "200"},
	{":status", "204"},
	{":status", "206"},
	{":status", "304"},
	{":status", "400"},
	{":status", "404"},
	{":status", "500"},
	{"accept-charset", ""},
	{"accept-encoding", "gzip, deflate"},
	{"accept-language", ""},
	{"accept-ranges", ""},
	{"accept", ""},
	{"access-control-allow-origin", ""},
	{"age", ""},
	{"allow", ""},
	{"authorization", ""},
	{"cache-control", ""},
	{"content-disposition", ""},
	{"content-encoding", ""},
	{"content-language", ""},
	{"content-length", ""},
	{"content-location", ""},
	{"content-range", ""},
	{"content-type", ""},
	{"cookie", ""},
	{"date", ""},
	{"etag", ""},
	{"expect", ""},
	{"expires", ""},
	{"from", ""},
	{"host", ""},
	{"if-match", ""},
	{"if-modified-since", ""},
	{"if-none-match", ""},
	{"if-range", ""},
	{"if-unmodified-since", ""},
	{"last-modified", ""},
	{"link", ""},
	{"location", ""},
	{"max-forwards", ""},
	{"proxy-authenticate", ""},
	{"proxy-authorization", ""},
	{"range", ""},
	{"referer", ""},
	{"refresh", ""},
	{"retry-after", ""},
	{"server", ""},
	{"set-cookie", ""},
	{"strict-transport-security", ""},
	{"transfer-encoding", ""},
	{"user-agent", ""},
	{"vary", ""},
	{"via", ""},
	{"www-authenticate", ""},
}
var STATIC_TABLE_NUM byte = byte(len(STATIC_TABLE))
<commit_msg>inline insertFirst<commit_after>package GoHPACK

import (
	"github.com/ami-GS/GoHPACK/huffman"
)

type Header struct {
	Name, Value string
}

func (h Header) size() uint32 {
	return uint32(len(h.Name + h.Value))
}

type Table struct {
	head, tail       *RingTable
	currentEntrySize uint32
	currentEntryNum  uint32
	dynamicTableSize uint32
	Huffman          *huffman.Node
}

type RingTable struct {
	header    Header
	Next, Pre *RingTable
}

func NewTable() (t Table) {
	t.currentEntryNum = 0
	t.currentEntrySize = 0
	t.dynamicTableSize = 4096
	t.Huffman = &huffman.Node{nil, nil, -1} // temporally
	t.Huffman.CreateTree()
	return
}

func (t *Table) FindHeader(h Header) (match bool, index int) {
	//here should be optimized
	preName := ""
	for i, header := range STATIC_TABLE {
		if header == h {
			return true, i
		} else if header.Name == h.Name && index == 0 {
			preName = header.Name
			index = i
			match = false
		} else if index != 0 && preName != header.Name {
			return match, index
		}
	}

	ring := t.head
	for i := 0; i < int(t.currentEntryNum); i++ {
		if ring.header == h {
			return true, i + int(STATIC_TABLE_NUM)
		} else if ring.header.Name == h.Name && index == 0 {
			match = false
			index = i + int(STATIC_TABLE_NUM)
		}
		ring = ring.Next
	}
	if index > 0 {
		return match, index
	}
	return false, -1 // not found on any table
}

func (t *Table) GetHeader(index uint32) Header {
	if 0 < index && index < uint32(STATIC_TABLE_NUM) {
		return STATIC_TABLE[index]
	} else if uint32(STATIC_TABLE_NUM) <= index && index <= uint32(STATIC_TABLE_NUM+byte(t.currentEntryNum)) {
		// get from dynamic table
		ring := t.head
		for i := uint32(0); i < index-uint32(STATIC_TABLE_NUM); i++ {
			ring = ring.Next
		}
		return ring.header
	}

	panic("error")
}

func (t *Table) getFromDynamicTable(index uint32) Header {
	index -= uint32(STATIC_TABLE_NUM)
	ring := t.head
	for i := uint32(0); i < index; i++ {
		ring = ring.Next
	}
	return ring.header
}

var nilElem *RingTable

func (t *Table) delLast() {
	t.currentEntrySize -= t.tail.header.size()
	deleated := t.tail
	t.tail = deleated.Pre
	deleated = nilElem
	t.currentEntryNum--
}

func (t *Table) AddHeader(header Header) {
	for t.currentEntrySize+header.size() > t.dynamicTableSize {
		t.delLast()
	}
	// insert first
	elem := RingTable{header, nil, nil}
	if t.currentEntryNum >= 1 {
		elem.Next = t.head
		t.head.Pre = &elem
	}
	t.head = &elem

	if t.currentEntryNum == 0 {
		t.tail = &elem
	}
	t.currentEntryNum++
	t.currentEntrySize += header.size()
}

func (t *Table) PackContent(content string, toHuffman bool) []byte {
	if toHuffman {
		encoded, length := t.Huffman.Encode(content)
		intRep := PackIntRepresentation(uint32(length), 7)
		intRep[0] |= 0x80
		return append(intRep, encoded...)
	}
	intRep := PackIntRepresentation(uint32(len(content)), 7)
	return append(intRep, []byte(content)...)
}

func (t *Table) SetDynamicTableSize(size uint32) {
	t.dynamicTableSize = size
func (t *Table) ParseFromByte(buf []byte) (content string, cursor uint32) {
	length, cursor := ParseIntRepresentation(buf, 7)

	if buf[0]&0x80 > 0 {
		content = t.Huffman.Decode(buf[cursor:], length)
	} else {
		content = string(buf[cursor : cursor+length])
	}
	cursor += length
	return
}

func (table *Table) ParseHeader(index uint32, buf []byte, isIndexed bool) (name, value string, cursor uint32) {
	if c := uint32(0); !isIndexed {
		if index == 0 {
			name, c = table.ParseFromByte(buf[cursor:])
			cursor += c
		}
		value, c = table.ParseFromByte(buf[cursor:])
		cursor += c
	}

	if index > 0 {
		header := table.GetHeader(index)
		name = header.Name
		if len(value) == 0 {
			value = header.Value
		}
	}
	return
}

var STATIC_TABLE = &[...]Header{
	{"", ""},
	{":authority", ""},
	{":method", "GET"},
	{":method", "POST"},
	{":path", "/"},
	{":path", "/index.html"},
	{":scheme", "http"},
	{":scheme", "https"},
	{":status", "200"},
	{":status", "204"},
	{":status", "206"},
	{":status", "304"},
	{":status", "400"},
	{":status", "404"},
	{":status", "500"},
	{"accept-charset", ""},
	{"accept-encoding", "gzip, deflate"},
	{"accept-language", ""},
	{"accept-ranges", ""},
	{"accept", ""},
	{"access-control-allow-origin", ""},
	{"age", ""},
	{"allow", ""},
	{"authorization", ""},
	{"cache-control", ""},
	{"content-disposition", ""},
	{"content-encoding", ""},
	{"content-language", ""},
	{"content-length", ""},
	{"content-location", ""},
	{"content-range", ""},
	{"content-type", ""},
	{"cookie", ""},
	{"date", ""},
	{"etag", ""},
	{"expect", ""},
	{"expires", ""},
	{"from", ""},
	{"host", ""},
	{"if-match", ""},
	{"if-modified-since", ""},
	{"if-none-match", ""},
	{"if-range", ""},
	{"if-unmodified-since", ""},
	{"last-modified", ""},
	{"link", ""},
	{"location", ""},
	{"max-forwards", ""},
	{"proxy-authenticate", ""},
	{"proxy-authorization", ""},
	{"range", ""},
	{"referer", ""},
	{"refresh", ""},
	{"retry-after", ""},
	{"server", ""},
	{"set-cookie", ""},
	{"strict-transport-security", ""},
	{"transfer-encoding", ""},
	{"user-agent", ""},
	{"vary", ""},
	{"via", ""},
	{"www-authenticate", ""},
}
var STATIC_TABLE_NUM byte = byte(len(STATIC_TABLE))
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"crypto/sha256"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/jpillora/backoff"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

type LogEvent struct {
	Pod       *v1.Pod
	Container *v1.Container
	Timestamp *time.Time
	Message   string
}

type LogEventFunc func(LogEvent)

func NewContainerTailer(
	client kubernetes.Interface,
	pod v1.Pod,
	container v1.Container,
	eventFunc LogEventFunc,
	fromTimestamp *time.Time) *ContainerTailer {
	return &ContainerTailer{
		client:        client,
		pod:           pod,
		container:     container,
		eventFunc:     eventFunc,
		fromTimestamp: fromTimestamp,
		errorBackoff:  &backoff.Backoff{},
	}
}

type ContainerTailer struct {
	client           kubernetes.Interface
	pod              v1.Pod
	container        v1.Container
	stop             bool
	eventFunc        LogEventFunc
	fromTimestamp    *time.Time
	errorBackoff     *backoff.Backoff
	lastLineChecksum []byte
	restarted        bool
}

func (ct *ContainerTailer) Stop() {
	ct.stop = true
}

func (ct *ContainerTailer) Run(onError func(err error)) {
	ct.errorBackoff.Reset()
	for !ct.stop {
		stream, err := ct.getStream()
		if err != nil {
			time.Sleep(ct.errorBackoff.Duration())
			onError(err)
			continue
		}
		if stream == nil {
			break
		}
		if err := ct.runStream(stream); err != nil {
			onError(err)
			time.Sleep(ct.errorBackoff.Duration())
		}
		ct.restarted = true
	}
}

func (ct *ContainerTailer) runStream(stream io.ReadCloser) error {
	defer func() {
		_ = stream.Close()
	}()

	r := bufio.NewReader(stream)
	for {
		line, err := r.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
		ct.errorBackoff.Reset()

		if ct.restarted {
			// If just restarted, we might be continuing off a timestamp that results in dupes,
			// so discard the dupes.
			ct.restarted = false
			checksum := checksumLine(line)
			if bytes.Equal(ct.lastLineChecksum, checksum) {
				continue
			}
			ct.lastLineChecksum = checksum
		}
		ct.receiveLine(line)
	}
	return nil
}

func (ct *ContainerTailer) receiveLine(s string) {
	if len(s) > 0 && s[len(s)-1] == '\n' {
		s = s[0 : len(s)-1]
	}
	for len(s) > 0 && s[len(s)-1] == '\r' {
		s = s[0 : len(s)-1]
	}

	parts := strings.SplitN(s, " ", 2)
	if len(parts) < 2 {
		// TODO: Warn?
		return
	}

	var timestamp *time.Time
	if t, err := time.Parse(time.RFC3339Nano, parts[0]); err == nil {
		timestamp = &t
		if ct.fromTimestamp != nil && timestamp.Before(*ct.fromTimestamp) {
			// We are receiving an old line, skip it
			return
		}

		// On restart, start from this timestamp. This isn't exact, however.
		nextTimestamp := t.Add(time.Millisecond * 1)
		ct.fromTimestamp = &nextTimestamp
	}

	ct.eventFunc(LogEvent{
		Pod:       &ct.pod,
		Container: &ct.container,
		Timestamp: timestamp,
		Message:   parts[1],
	})
}

func (ct *ContainerTailer) getStream() (io.ReadCloser, error) {
	var sinceTime *metav1.Time
	if ct.fromTimestamp != nil {
		sinceTime = &metav1.Time{
			Time: ct.fromTimestamp.UTC(),
		}
	}

	boff := &backoff.Backoff{}
	for {
		stream, err := ct.client.CoreV1().Pods(ct.pod.Namespace).GetLogs(ct.pod.Name, &v1.PodLogOptions{
			Container:  ct.container.Name,
			Follow:     true,
			Timestamps: true,
			SinceTime:  sinceTime,
		}).Stream()
		if err == nil {
			return stream, nil
		}
		if status, ok := err.(errors.APIStatus); ok {
			// This will happen if the pod isn't ready for log-reading yet
			switch status.Status().Code {
			case http.StatusBadRequest:
				time.Sleep(boff.Duration())
				continue
			case http.StatusNotFound:
				return nil, nil
			}
		}
		return nil, err
	}
}

func checksumLine(s string) []byte {
	digest := sha256.New()
	digest.Write([]byte(s))
	return digest.Sum(nil)
}
<commit_msg>Fix the "smart" recovery logic to not ignore lines with near timestamps when not actually recovering.<commit_after>package main

import (
	"bufio"
	"bytes"
	"crypto/sha256"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/jpillora/backoff"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

type tailState int

const (
	tailStateNormal tailState = iota
	tailStateRecover
)

type LogEvent struct {
	Pod       *v1.Pod
	Container *v1.Container
	Timestamp *time.Time
	Message   string
}

type LogEventFunc func(LogEvent)

func NewContainerTailer(
	client kubernetes.Interface,
	pod v1.Pod,
	container v1.Container,
	eventFunc LogEventFunc,
	fromTimestamp *time.Time) *ContainerTailer {
	return &ContainerTailer{
		client:        client,
		pod:           pod,
		container:     container,
		eventFunc:     eventFunc,
		fromTimestamp: fromTimestamp,
		errorBackoff:  &backoff.Backoff{},
		state:         tailStateNormal,
	}
}

type ContainerTailer struct {
	client           kubernetes.Interface
	pod              v1.Pod
	container        v1.Container
	stop             bool
	eventFunc        LogEventFunc
	fromTimestamp    *time.Time
	errorBackoff     *backoff.Backoff
	lastLineChecksum []byte
	state            tailState
}

func (ct *ContainerTailer) Stop() {
	ct.stop = true
}

func (ct *ContainerTailer) Run(onError func(err error)) {
	ct.errorBackoff.Reset()
	for !ct.stop {
		stream, err := ct.getStream()
		if err != nil {
			time.Sleep(ct.errorBackoff.Duration())
			onError(err)
			continue
		}
		if stream == nil {
			break
		}
		if err := ct.runStream(stream); err != nil {
			onError(err)
			time.Sleep(ct.errorBackoff.Duration())
		}
		ct.state = tailStateRecover
	}
}

func (ct *ContainerTailer) runStream(stream io.ReadCloser) error {
	defer func() {
		_ = stream.Close()
	}()

	scanner := bufio.NewScanner(stream)
	for scanner.Scan() {
		line := scanner.Text()
		ct.errorBackoff.Reset()
		ct.receiveLine(line)
	}
	if err := scanner.Err(); err != nil && err != io.EOF {
		return err
	}
	return nil
}

func (ct *ContainerTailer) receiveLine(s string) {
	if len(s) > 0 && s[len(s)-1] == '\n' {
		s = s[0 : len(s)-1]
	}
	for len(s) > 0 && s[len(s)-1] == '\r' {
		s = s[0 : len(s)-1]
	}

	parts := strings.SplitN(s, " ", 2)
	if len(parts) < 2 {
		// TODO: Warn
		return
	}

	timeString, message := parts[0], parts[1]

	var timestamp time.Time
	if t, err := time.Parse(time.RFC3339Nano, timeString); err == nil {
		timestamp = t
	} else {
		// TODO: Warn
		return
	}

	checksum := checksumLine(message)

	if ct.state == tailStateRecover {
		if ct.lastLineChecksum != nil && bytes.Equal(ct.lastLineChecksum, checksum) {
			// If just restarted, we might be continuing off a timestamp that results in dupes,
			// so discard the dupes.
			return
		}
		if ct.fromTimestamp != nil && timestamp.Before(*ct.fromTimestamp) {
			// We are receiving an old line, skip it
			return
		}
	}

	ct.lastLineChecksum = checksum
	ct.state = tailStateNormal

	// On restart, start from this timestamp. This isn't exact, however.
	nextTimestamp := timestamp.Add(time.Millisecond * 1)
	ct.fromTimestamp = &nextTimestamp

	ct.eventFunc(LogEvent{
		Pod:       &ct.pod,
		Container: &ct.container,
		Timestamp: &timestamp,
		Message:   parts[1],
	})
}

func (ct *ContainerTailer) getStream() (io.ReadCloser, error) {
	var sinceTime *metav1.Time
	if ct.fromTimestamp != nil {
		sinceTime = &metav1.Time{
			Time: ct.fromTimestamp.UTC(),
		}
	}

	boff := &backoff.Backoff{}
	for {
		stream, err := ct.client.CoreV1().Pods(ct.pod.Namespace).GetLogs(ct.pod.Name, &v1.PodLogOptions{
			Container:  ct.container.Name,
			Follow:     true,
			Timestamps: true,
			SinceTime:  sinceTime,
		}).Stream()
		if err == nil {
			return stream, nil
		}
		if status, ok := err.(errors.APIStatus); ok {
			// This will happen if the pod isn't ready for log-reading yet
			switch status.Status().Code {
			case http.StatusBadRequest:
				time.Sleep(boff.Duration())
				continue
			case http.StatusNotFound:
				return nil, nil
			}
		}
		return nil, err
	}
}

func checksumLine(s string) []byte {
	digest := sha256.New()
	digest.Write([]byte(s))
	return digest.Sum(nil)
}
<|endoftext|>


<commit_before>package chromedp

import (
	"context"
	"encoding/json"
	"errors"
	"sync"
	"sync/atomic"

	"github.com/mailru/easyjson"

	"github.com/chromedp/cdproto"
	"github.com/chromedp/cdproto/cdp"
	"github.com/chromedp/cdproto/dom"
	"github.com/chromedp/cdproto/page"
	"github.com/chromedp/cdproto/runtime"
	"github.com/chromedp/cdproto/target"
)

// Target manages a Chrome DevTools Protocol target.
type Target struct {
	browser   *Browser
	SessionID target.SessionID
	TargetID  target.ID

	listenersMu sync.Mutex
	listeners   []cancelableListener

	messageQueue chan *cdproto.Message

	// frameMu protects both frames and cur.
	frameMu sync.RWMutex
	// frames is the set of encountered frames.
	frames       map[cdp.FrameID]*cdp.Frame
	execContexts map[cdp.FrameID]runtime.ExecutionContextID
	// cur is the current top level frame.
	cur cdp.FrameID

	// logging funcs
	logf, errf func(string, ...interface{})

	// Indicates if the target is a worker target.
	isWorker bool
}

func (t *Target) enclosingFrame(node *cdp.Node) cdp.FrameID {
	t.frameMu.RLock()
	top := t.frames[t.cur]
	t.frameMu.RUnlock()
	top.RLock()
	defer top.RUnlock()
	for {
		if node == nil {
			// Avoid crashing. This can happen if we're using an old
			// node that has been replaced, for example.
			return ""
		}
		if node.FrameID != "" {
			break
		}
		node = top.Nodes[node.ParentID]
	}
	return node.FrameID
}

func (t *Target) run(ctx context.Context) {
	type eventValue struct {
		method cdproto.MethodType
		value  interface{}
	}
	// syncEventQueue is used to handle events synchronously within Target.
	// TODO: If this queue gets full, the goroutine below could get stuck on
	// a send, and response callbacks would never run, resulting in a
	// deadlock. Can we fix this without potentially using lots of memory?
	syncEventQueue := make(chan eventValue, 4096)

	// This goroutine receives events from the browser, calls listeners, and
	// then passes the events onto the main goroutine for the target handler
	// to update itself.
	go func() {
		for {
			select {
			case <-ctx.Done():
				return

			case msg := <-t.messageQueue:
				if msg.ID != 0 {
					t.listenersMu.Lock()
					t.listeners = runListeners(t.listeners, msg)
					t.listenersMu.Unlock()
					continue
				}
				ev, err := cdproto.UnmarshalMessage(msg)
				if err != nil {
					if _, ok := err.(cdp.ErrUnknownCommandOrEvent); ok {
						// This is most likely an event received from an older
						// Chrome which a newer cdproto doesn't have, as it is
						// deprecated. Ignore that error.
						continue
					}
					t.errf("could not unmarshal event: %v", err)
					continue
				}
				t.listenersMu.Lock()
				t.listeners = runListeners(t.listeners, ev)
				t.listenersMu.Unlock()

				switch msg.Method.Domain() {
				case "Runtime", "Page", "DOM":
					select {
					case <-ctx.Done():
						return
					case syncEventQueue <- eventValue{msg.Method, ev}:
					}
				}
			}
		}
	}()

	for {
		select {
		case <-ctx.Done():
			return
		case ev := <-syncEventQueue:
			switch ev.method.Domain() {
			case "Runtime":
				t.runtimeEvent(ev.value)
			case "Page":
				t.pageEvent(ev.value)
			case "DOM":
				t.domEvent(ctx, ev.value)
			}
		}
	}
}

func (t *Target) Execute(ctx context.Context, method string, params easyjson.Marshaler, res easyjson.Unmarshaler) error {
	if method == target.CommandCloseTarget {
		return errors.New("to close the target, cancel its context or use chromedp.Cancel")
	}

	id := atomic.AddInt64(&t.browser.next, 1)
	lctx, cancel := context.WithCancel(ctx)
	ch := make(chan *cdproto.Message, 1)
	fn := func(ev interface{}) {
		if msg, ok := ev.(*cdproto.Message); ok && msg.ID == id {
			select {
			case <-ctx.Done():
			case ch <- msg:
			}
			cancel()
		}
	}
	t.listenersMu.Lock()
	t.listeners = append(t.listeners, cancelableListener{lctx, fn})
	t.listenersMu.Unlock()

	// send command
	var buf []byte
	if params != nil {
		var err error
		buf, err = easyjson.Marshal(params)
		if err != nil {
			return err
		}
	}
	cmd := &cdproto.Message{
		ID:        id,
		SessionID: t.SessionID,
		Method:    cdproto.MethodType(method),
		Params:    buf,
	}
	select {
	case <-ctx.Done():
		return ctx.Err()
	case t.browser.cmdQueue <- cmd:
	}

	// wait for result
	select {
	case <-ctx.Done():
		return ctx.Err()
	case msg := <-ch:
		switch {
		case msg == nil:
			return ErrChannelClosed
		case msg.Error != nil:
			return msg.Error
		case res != nil:
			return easyjson.Unmarshal(msg.Result, res)
		}
	}
	return nil
}

// runtimeEvent handles incoming runtime events.
func (t *Target) runtimeEvent(ev interface{}) {
	switch ev := ev.(type) {
	case *runtime.EventExecutionContextCreated:
		var aux struct {
			FrameID cdp.FrameID
		}
		if len(ev.Context.AuxData) == 0 {
			break
		}
		if err := json.Unmarshal(ev.Context.AuxData, &aux); err != nil {
			t.errf("could not decode executionContextCreated auxData %q: %v", ev.Context.AuxData, err)
			break
		}
		if aux.FrameID != "" {
			t.frameMu.Lock()
			t.execContexts[aux.FrameID] = ev.Context.ID
			t.frameMu.Unlock()
		}
	case *runtime.EventExecutionContextDestroyed:
		t.frameMu.Lock()
		for frameID, ctxID := range t.execContexts {
			if ctxID == ev.ExecutionContextID {
				delete(t.execContexts, frameID)
			}
		}
		t.frameMu.Unlock()
	case *runtime.EventExecutionContextsCleared:
		t.frameMu.Lock()
		for frameID := range t.execContexts {
			delete(t.execContexts, frameID)
		}
		t.frameMu.Unlock()
	}
}

// documentUpdated handles the document updated event, retrieving the document
// root for the root frame.
func (t *Target) documentUpdated(ctx context.Context) {
	t.frameMu.RLock()
	f := t.frames[t.cur]
	t.frameMu.RUnlock()
	if f == nil {
		// TODO: This seems to happen on CI, when running the tests
		// under the headless-shell Docker image. Figure out why.
		t.errf("received DOM.documentUpdated when there's no top-level frame")
		return
	}
	f.Lock()
	defer f.Unlock()

	// invalidate nodes
	if f.Root != nil {
		close(f.Root.Invalidated)
	}

	f.Nodes = make(map[cdp.NodeID]*cdp.Node)
	var err error
	f.Root, err = dom.GetDocument().Do(cdp.WithExecutor(ctx, t))
	if err == context.Canceled {
		return // TODO: perhaps not necessary, but useful to keep the tests less noisy
	}
	if err != nil {
		t.errf("could not retrieve document root for %s: %v", f.ID, err)
		return
	}
	f.Root.Invalidated = make(chan struct{})
	walk(f.Nodes, f.Root)
}

// pageEvent handles incoming page events.
func (t *Target) pageEvent(ev interface{}) {
	var id cdp.FrameID
	var op frameOp

	switch e := ev.(type) {
	case *page.EventFrameNavigated:
		t.frameMu.Lock()
		t.frames[e.Frame.ID] = e.Frame
		if e.Frame.ParentID == "" {
			// This frame is only the new top-level frame if it has
			// no parent.
			t.cur = e.Frame.ID
		}
		t.frameMu.Unlock()
		return

	case *page.EventFrameAttached:
		id, op = e.FrameID, frameAttached(e.ParentFrameID)

	case *page.EventFrameDetached:
		id, op = e.FrameID, frameDetached

	case *page.EventFrameStartedLoading:
		id, op = e.FrameID, frameStartedLoading

	case *page.EventFrameStoppedLoading:
		id, op = e.FrameID, frameStoppedLoading

		// ignored events
	case *page.EventFrameRequestedNavigation:
		return
	case *page.EventDomContentEventFired:
		return
	case *page.EventLoadEventFired:
		return
	case *page.EventFrameResized:
		return
	case *page.EventLifecycleEvent:
		return
	case *page.EventNavigatedWithinDocument:
		return
	case *page.EventJavascriptDialogOpening:
		return
	case *page.EventJavascriptDialogClosed:
		return
	case *page.EventWindowOpen:
		return
	case *page.EventDownloadWillBegin:
		return
	case *page.EventDownloadProgress:
		return

	default:
		t.errf("unhandled page event %T", ev)
		return
	}

	t.frameMu.Lock()
	f := t.frames[id]
	if f == nil {
		// This can happen if a frame is attached or starts loading
		// before it's ever navigated to. We won't have all the frame
		// details just yet, but that's okay.
		f = &cdp.Frame{ID: id}
		t.frames[id] = f
	}
	t.frameMu.Unlock()

	f.Lock()
	op(f)
	f.Unlock()
}

// domEvent handles incoming DOM events.
func (t *Target) domEvent(ctx context.Context, ev interface{}) {
	f := t.frames[t.cur]
	var id cdp.NodeID
	var op nodeOp

	switch e := ev.(type) {
	case *dom.EventDocumentUpdated:
		t.documentUpdated(ctx)
		return

	case *dom.EventSetChildNodes:
		id, op = e.ParentID, setChildNodes(f.Nodes, e.Nodes)

	case *dom.EventAttributeModified:
		id, op = e.NodeID, attributeModified(e.Name, e.Value)

	case *dom.EventAttributeRemoved:
		id, op = e.NodeID, attributeRemoved(e.Name)

	case *dom.EventInlineStyleInvalidated:
		if len(e.NodeIds) == 0 {
			return
		}

		id, op = e.NodeIds[0], inlineStyleInvalidated(e.NodeIds[1:])

	case *dom.EventCharacterDataModified:
		id, op = e.NodeID, characterDataModified(e.CharacterData)

	case *dom.EventChildNodeCountUpdated:
		id, op = e.NodeID, childNodeCountUpdated(e.ChildNodeCount)

	case *dom.EventChildNodeInserted:
		id, op = e.ParentNodeID, childNodeInserted(f.Nodes, e.PreviousNodeID, e.Node)

	case *dom.EventChildNodeRemoved:
		id, op = e.ParentNodeID, childNodeRemoved(f.Nodes, e.NodeID)

	case *dom.EventShadowRootPushed:
		id, op = e.HostID, shadowRootPushed(f.Nodes, e.Root)

	case *dom.EventShadowRootPopped:
		id, op = e.HostID, shadowRootPopped(f.Nodes, e.RootID)

	case *dom.EventPseudoElementAdded:
		id, op = e.ParentID, pseudoElementAdded(f.Nodes, e.PseudoElement)

	case *dom.EventPseudoElementRemoved:
		id, op = e.ParentID, pseudoElementRemoved(f.Nodes, e.PseudoElementID)

	case *dom.EventDistributedNodesUpdated:
		id, op = e.InsertionPointID, distributedNodesUpdated(e.DistributedNodes)

	default:
		t.errf("unhandled node event %T", ev)
		return
	}

	n, ok := f.Nodes[id]
	if !ok {
		// Node ID has been invalidated. Nothing to do.
		return
	}

	f.Lock()
	op(n)
	f.Unlock()
}
<commit_msg>Adding ignore for experimental page event<commit_after>package chromedp

import (
	"context"
	"encoding/json"
	"errors"
	"sync"
	"sync/atomic"

	"github.com/mailru/easyjson"

	"github.com/chromedp/cdproto"
	"github.com/chromedp/cdproto/cdp"
	"github.com/chromedp/cdproto/dom"
	"github.com/chromedp/cdproto/page"
	"github.com/chromedp/cdproto/runtime"
	"github.com/chromedp/cdproto/target"
)

// Target manages a Chrome DevTools Protocol target.
type Target struct {
	browser   *Browser
	SessionID target.SessionID
	TargetID  target.ID

	listenersMu sync.Mutex
	listeners   []cancelableListener

	messageQueue chan *cdproto.Message

	// frameMu protects both frames and cur.
	frameMu sync.RWMutex
	// frames is the set of encountered frames.
	frames       map[cdp.FrameID]*cdp.Frame
	execContexts map[cdp.FrameID]runtime.ExecutionContextID
	// cur is the current top level frame.
	cur cdp.FrameID

	// logging funcs
	logf, errf func(string, ...interface{})

	// Indicates if the target is a worker target.
	isWorker bool
}

func (t *Target) enclosingFrame(node *cdp.Node) cdp.FrameID {
	t.frameMu.RLock()
	top := t.frames[t.cur]
	t.frameMu.RUnlock()
	top.RLock()
	defer top.RUnlock()
	for {
		if node == nil {
			// Avoid crashing. This can happen if we're using an old
			// node that has been replaced, for example.
			return ""
		}
		if node.FrameID != "" {
			break
		}
		node = top.Nodes[node.ParentID]
	}
	return node.FrameID
}

func (t *Target) run(ctx context.Context) {
	type eventValue struct {
		method cdproto.MethodType
		value  interface{}
	}
	// syncEventQueue is used to handle events synchronously within Target.
	// TODO: If this queue gets full, the goroutine below could get stuck on
	// a send, and response callbacks would never run, resulting in a
	// deadlock. Can we fix this without potentially using lots of memory?
	syncEventQueue := make(chan eventValue, 4096)

	// This goroutine receives events from the browser, calls listeners, and
	// then passes the events onto the main goroutine for the target handler
	// to update itself.
	go func() {
		for {
			select {
			case <-ctx.Done():
				return

			case msg := <-t.messageQueue:
				if msg.ID != 0 {
					t.listenersMu.Lock()
					t.listeners = runListeners(t.listeners, msg)
					t.listenersMu.Unlock()
					continue
				}
				ev, err := cdproto.UnmarshalMessage(msg)
				if err != nil {
					if _, ok := err.(cdp.ErrUnknownCommandOrEvent); ok {
						// This is most likely an event received from an older
						// Chrome which a newer cdproto doesn't have, as it is
						// deprecated. Ignore that error.
						continue
					}
					t.errf("could not unmarshal event: %v", err)
					continue
				}
				t.listenersMu.Lock()
				t.listeners = runListeners(t.listeners, ev)
				t.listenersMu.Unlock()

				switch msg.Method.Domain() {
				case "Runtime", "Page", "DOM":
					select {
					case <-ctx.Done():
						return
					case syncEventQueue <- eventValue{msg.Method, ev}:
					}
				}
			}
		}
	}()

	for {
		select {
		case <-ctx.Done():
			return
		case ev := <-syncEventQueue:
			switch ev.method.Domain() {
			case "Runtime":
				t.runtimeEvent(ev.value)
			case "Page":
				t.pageEvent(ev.value)
			case "DOM":
				t.domEvent(ctx, ev.value)
			}
		}
	}
}

func (t *Target) Execute(ctx context.Context, method string, params easyjson.Marshaler, res easyjson.Unmarshaler) error {
	if method == target.CommandCloseTarget {
		return errors.New("to close the target, cancel its context or use chromedp.Cancel")
	}

	id := atomic.AddInt64(&t.browser.next, 1)
	lctx, cancel := context.WithCancel(ctx)
	ch := make(chan *cdproto.Message, 1)
	fn := func(ev interface{}) {
		if msg, ok := ev.(*cdproto.Message); ok && msg.ID == id {
			select {
			case <-ctx.Done():
			case ch <- msg:
			}
			cancel()
		}
	}
	t.listenersMu.Lock()
	t.listeners = append(t.listeners, cancelableListener{lctx, fn})
	t.listenersMu.Unlock()

	// send command
	var buf []byte
	if params != nil {
		var err error
		buf, err = easyjson.Marshal(params)
		if err != nil {
			return err
		}
	}
	cmd := &cdproto.Message{
		ID:        id,
		SessionID: t.SessionID,
		Method:    cdproto.MethodType(method),
		Params:    buf,
	}
	select {
	case <-ctx.Done():
		return ctx.Err()
	case t.browser.cmdQueue <- cmd:
	}

	// wait for result
	select {
	case <-ctx.Done():
		return ctx.Err()
	case msg := <-ch:
		switch {
		case msg == nil:
			return ErrChannelClosed
		case msg.Error != nil:
			return msg.Error
		case res != nil:
			return easyjson.Unmarshal(msg.Result, res)
		}
	}
	return nil
}

// runtimeEvent handles incoming runtime events.
func (t *Target) runtimeEvent(ev interface{}) {
	switch ev := ev.(type) {
	case *runtime.EventExecutionContextCreated:
		var aux struct {
			FrameID cdp.FrameID
		}
		if len(ev.Context.AuxData) == 0 {
			break
		}
		if err := json.Unmarshal(ev.Context.AuxData, &aux); err != nil {
			t.errf("could not decode executionContextCreated auxData %q: %v", ev.Context.AuxData, err)
			break
		}
		if aux.FrameID != "" {
			t.frameMu.Lock()
			t.execContexts[aux.FrameID] = ev.Context.ID
			t.frameMu.Unlock()
		}
	case *runtime.EventExecutionContextDestroyed:
		t.frameMu.Lock()
		for frameID, ctxID := range t.execContexts {
			if ctxID == ev.ExecutionContextID {
				delete(t.execContexts, frameID)
			}
		}
		t.frameMu.Unlock()
	case *runtime.EventExecutionContextsCleared:
		t.frameMu.Lock()
		for frameID := range t.execContexts {
			delete(t.execContexts, frameID)
		}
		t.frameMu.Unlock()
	}
}

// documentUpdated handles the document updated event, retrieving the document
// root for the root frame.
func (t *Target) documentUpdated(ctx context.Context) {
	t.frameMu.RLock()
	f := t.frames[t.cur]
	t.frameMu.RUnlock()
	if f == nil {
		// TODO: This seems to happen on CI, when running the tests
		// under the headless-shell Docker image. Figure out why.
		t.errf("received DOM.documentUpdated when there's no top-level frame")
		return
	}
	f.Lock()
	defer f.Unlock()

	// invalidate nodes
	if f.Root != nil {
		close(f.Root.Invalidated)
	}

	f.Nodes = make(map[cdp.NodeID]*cdp.Node)
	var err error
	f.Root, err = dom.GetDocument().Do(cdp.WithExecutor(ctx, t))
	if err == context.Canceled {
		return // TODO: perhaps not necessary, but useful to keep the tests less noisy
	}
	if err != nil {
		t.errf("could not retrieve document root for %s: %v", f.ID, err)
		return
	}
	f.Root.Invalidated = make(chan struct{})
	walk(f.Nodes, f.Root)
}

// pageEvent handles incoming page events.
func (t *Target) pageEvent(ev interface{}) {
	var id cdp.FrameID
	var op frameOp

	switch e := ev.(type) {
	case *page.EventFrameNavigated:
		t.frameMu.Lock()
		t.frames[e.Frame.ID] = e.Frame
		if e.Frame.ParentID == "" {
			// This frame is only the new top-level frame if it has
			// no parent.
			t.cur = e.Frame.ID
		}
		t.frameMu.Unlock()
		return

	case *page.EventFrameAttached:
		id, op = e.FrameID, frameAttached(e.ParentFrameID)

	case *page.EventFrameDetached:
		id, op = e.FrameID, frameDetached

	case *page.EventFrameStartedLoading:
		id, op = e.FrameID, frameStartedLoading

	case *page.EventFrameStoppedLoading:
		id, op = e.FrameID, frameStoppedLoading

		// ignored events
	case *page.EventFrameRequestedNavigation:
		return
	case *page.EventDomContentEventFired:
		return
	case *page.EventLoadEventFired:
		return
	case *page.EventFrameResized:
		return
	case *page.EventLifecycleEvent:
		return
	case *page.EventNavigatedWithinDocument:
		return
	case *page.EventJavascriptDialogOpening:
		return
	case *page.EventJavascriptDialogClosed:
		return
	case *page.EventWindowOpen:
		return
	case *page.EventDownloadWillBegin:
		return
	case *page.EventDownloadProgress:
		return
	case *page.EventDocumentOpened:
		return

	default:
		t.errf("unhandled page event %T", ev)
		return
	}

	t.frameMu.Lock()
	f := t.frames[id]
	if f == nil {
		// This can happen if a frame is attached or starts loading
		// before it's ever navigated to. We won't have all the frame
		// details just yet, but that's okay.
		f = &cdp.Frame{ID: id}
		t.frames[id] = f
	}
	t.frameMu.Unlock()

	f.Lock()
	op(f)
	f.Unlock()
}

// domEvent handles incoming DOM events.
func (t *Target) domEvent(ctx context.Context, ev interface{}) {
	f := t.frames[t.cur]
	var id cdp.NodeID
	var op nodeOp

	switch e := ev.(type) {
	case *dom.EventDocumentUpdated:
		t.documentUpdated(ctx)
		return

	case *dom.EventSetChildNodes:
		id, op = e.ParentID, setChildNodes(f.Nodes, e.Nodes)

	case *dom.EventAttributeModified:
		id, op = e.NodeID, attributeModified(e.Name, e.Value)

	case *dom.EventAttributeRemoved:
		id, op = e.NodeID, attributeRemoved(e.Name)

	case *dom.EventInlineStyleInvalidated:
		if len(e.NodeIds) == 0 {
			return
		}

		id, op = e.NodeIds[0], inlineStyleInvalidated(e.NodeIds[1:])

	case *dom.EventCharacterDataModified:
		id, op = e.NodeID, characterDataModified(e.CharacterData)

	case *dom.EventChildNodeCountUpdated:
		id, op = e.NodeID, childNodeCountUpdated(e.ChildNodeCount)

	case *dom.EventChildNodeInserted:
		id, op = e.ParentNodeID, childNodeInserted(f.Nodes, e.PreviousNodeID, e.Node)

	case *dom.EventChildNodeRemoved:
		id, op = e.ParentNodeID, childNodeRemoved(f.Nodes, e.NodeID)

	case *dom.EventShadowRootPushed:
		id, op = e.HostID, shadowRootPushed(f.Nodes, e.Root)

	case *dom.EventShadowRootPopped:
		id, op = e.HostID, shadowRootPopped(f.Nodes, e.RootID)

	case *dom.EventPseudoElementAdded:
		id, op = e.ParentID, pseudoElementAdded(f.Nodes, e.PseudoElement)

	case *dom.EventPseudoElementRemoved:
		id, op = e.ParentID, pseudoElementRemoved(f.Nodes, e.PseudoElementID)

	case *dom.EventDistributedNodesUpdated:
		id, op = e.InsertionPointID, distributedNodesUpdated(e.DistributedNodes)

	default:
		t.errf("unhandled node event %T", ev)
		return
	}

	n, ok := f.Nodes[id]
	if !ok {
		// Node ID has been invalidated. Nothing to do.
		return
	}

	f.Lock()
	op(n)
	f.Unlock()
}
<|endoftext|>


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

package main

import (
	"fmt"
	"time"

	"github.com/cheggaaa/pb"
	"github.com/minio-io/cli"
	"github.com/minio-io/mc/pkg/client"
)

const (
	printDate = "2006-01-02 15:04:05 MST"
)

// printBuckets lists buckets and its meta-dat
func printBuckets(v []*client.Bucket) {
	for _, b := range v {
		msg := fmt.Sprintf("%23s %13s %s", b.CreationDate.Local().Format(printDate), "", b.Name)
		info(msg)
	}
}

// printObjects prints a meta-data of a list of objects
func printObjects(v []*client.Item) {
	if len(v) > 0 {
		// Items are already sorted
		for _, b := range v {
			printObject(b.LastModified, b.Size, b.Key)
		}
	}
}

// printObject prints object meta-data
func printObject(date time.Time, v int64, key string) {
	msg := fmt.Sprintf("%23s %13s %s", date.Local().Format(printDate), pb.FormatBytes(v), key)
	info(msg)
}

// doListCmd lists objects inside a bucket
func doListCmd(ctx *cli.Context) {
	var items []*client.Item

	urlStr, err := parseURL(ctx.Args().First())
	if err != nil {
		fatal(err.Error())
	}

	bucketName, objectName, err := url2Object(urlStr)
	if err != nil {
		fatal(err.Error())
	}

	client, err := getNewClient(globalDebugFlag, urlStr)
	if err != nil {
		fatal(err.Error())
	}

	if bucketName == "" { // List all buckets
		buckets, err := client.ListBuckets()
		if err != nil {
			fatal(err.Error())
		}
		printBuckets(buckets)
	} else {
		items, err = client.ListObjects(bucketName, objectName)
		if err != nil {
			fatal(err.Error())
		}
		printObjects(items)
	}
}
<commit_msg>Adding iodine to cmd-ls.go<commit_after>/*
 * Minimalist Object Storage, (C) 2014,2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"fmt"
	"time"

	"github.com/cheggaaa/pb"
	"github.com/minio-io/cli"
	"github.com/minio-io/iodine"
	"github.com/minio-io/mc/pkg/client"
	"github.com/minio-io/minio/pkg/utils/log"
)

const (
	printDate = "2006-01-02 15:04:05 MST"
)

// printBuckets lists buckets and its meta-dat
func printBuckets(v []*client.Bucket) {
	for _, b := range v {
		msg := fmt.Sprintf("%23s %13s %s", b.CreationDate.Local().Format(printDate), "", b.Name)
		info(msg)
	}
}

// printObjects prints a meta-data of a list of objects
func printObjects(v []*client.Item) {
	if len(v) > 0 {
		// Items are already sorted
		for _, b := range v {
			printObject(b.LastModified, b.Size, b.Key)
		}
	}
}

// printObject prints object meta-data
func printObject(date time.Time, v int64, key string) {
	msg := fmt.Sprintf("%23s %13s %s", date.Local().Format(printDate), pb.FormatBytes(v), key)
	info(msg)
}

// doListCmd lists objects inside a bucket
func doListCmd(ctx *cli.Context) {
	var items []*client.Item

	urlStr, err := parseURL(ctx.Args().First())
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		fatal(err.Error())
	}

	bucketName, objectName, err := url2Object(urlStr)
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		fatal(err.Error())
	}

	client, err := getNewClient(globalDebugFlag, urlStr)
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		fatal(err.Error())
	}

	if bucketName == "" { // List all buckets
		buckets, err := client.ListBuckets()
		if err != nil {
			log.Debug.Println(err)
			fatal(err.Error())
		}
		printBuckets(buckets)
	} else {
		items, err = client.ListObjects(bucketName, objectName)
		if err != nil {
			log.Debug.Println(err)
			fatal(err.Error())
		}
		printObjects(items)
	}
}
<|endoftext|>


<commit_before>package glogger

var EscapeCodes = map[string]string{
	"reset":          "\033[0m",
	"bold":           "\033[01m",
	"dim":            "\033[02m",
	"underlined":     "\033[04m",
	"blink":          "\033[05m",
	"reverse":        "\033[07m",
	"hidden":         "\033[08m",
	"black":          "\033[30m",
	"red":            "\033[31m",
	"green":          "\033[32m",
	"yellow":         "\033[33m",
	"blue":           "\033[34m",
	"purple":         "\033[35m",
	"cyan":           "\033[36m",
	"white":          "\033[37m",
	"bold_black":     "\033[30;01m",
	"bold_red":       "\033[31;01m",
	"bold_green":     "\033[32;01m",
	"bold_yellow":    "\033[33;01m",
	"bold_blue":      "\033[34;01m",
	"bold_purple":    "\033[35;01m",
	"bold_cyan":      "\033[36;01m",
	"bold_white":     "\033[37;01m",
	"bg_black":       "\033[40m",
	"bg_red":         "\033[41m",
	"bg_green":       "\033[42m",
	"bg_yellow":      "\033[43m",
	"bg_blue":        "\033[44m",
	"bg_purple":      "\033[45m",
	"bg_cyan":        "\033[46m",
	"bg_white":       "\033[47m",
	"bg_bold_black":  "\033[40;01m",
	"bg_bold_red":    "\033[41;01m",
	"bg_bold_green":  "\033[42;01m",
	"bg_bold_yellow": "\033[43;01m",
	"bg_bold_blue":   "\033[44;01m",
	"bg_bold_purple": "\033[45;01m",
	"bg_bold_cyan":   "\033[46;01m",
	"bg_bold_white":  "\033[47;01m",
}
<commit_msg>new color code<commit_after>package glogger

var EscapeCodes = map[string]string{
	"reset":          "\033[0m",
	"bold":           "\033[01m",
	"dim":            "\033[02m",
	"underlined":     "\033[04m",
	"blink":          "\033[05m",
	"reverse":        "\033[07m",
	"hidden":         "\033[08m",
	"black":          "\033[30m",
	"red":            "\033[31m",
	"green":          "\033[32m",
	"yellow":         "\033[33m",
	"blue":           "\033[34m",
	"purple":         "\033[35m",
	"cyan":           "\033[36m",
	"white":          "\033[37m",
	"bold_black":     "\033[30;01m",
	"bold_red":       "\033[31;01m",
	"bold_green":     "\033[32;01m",
	"bold_yellow":    "\033[33;01m",
	"bold_blue":      "\033[34;01m",
	"bold_purple":    "\033[35;01m",
	"bold_cyan":      "\033[36;01m",
	"bold_white":     "\033[37;01m",
	"dim_black":      "\033[30;02m",
	"dim_red":        "\033[31;02m",
	"dim_green":      "\033[32;02m",
	"dim_yellow":     "\033[33;02m",
	"dim_blue":       "\033[34;02m",
	"dim_purple":     "\033[35;02m",
	"dim_cyan":       "\033[36;02m",
	"dim_white":      "\033[37;02m",
	"bg_black":       "\033[40m",
	"bg_red":         "\033[41m",
	"bg_green":       "\033[42m",
	"bg_yellow":      "\033[43m",
	"bg_blue":        "\033[44m",
	"bg_purple":      "\033[45m",
	"bg_cyan":        "\033[46m",
	"bg_white":       "\033[47m",
	"bg_bold_black":  "\033[40;01m",
	"bg_bold_red":    "\033[41;01m",
	"bg_bold_green":  "\033[42;01m",
	"bg_bold_yellow": "\033[43;01m",
	"bg_bold_blue":   "\033[44;01m",
	"bg_bold_purple": "\033[45;01m",
	"bg_bold_cyan":   "\033[46;01m",
	"bg_bold_white":  "\033[47;01m",
	"bg_dim_black":   "\033[40;02m",
	"bg_dim_red":     "\033[41;02m",
	"bg_dim_green":   "\033[42;02m",
	"bg_dim_yellow":  "\033[43;02m",
	"bg_dim_blue":    "\033[44;02m",
	"bg_dim_purple":  "\033[45;02m",
	"bg_dim_cyan":    "\033[46;02m",
	"bg_dim_white":   "\033[47;02m",
}
<|endoftext|>