<commit_before>package main

import (
	"fmt"

	"github.com/frenata/gaga"
	. "github.com/frenata/gaga/decktet"
)

type AdamanPlayer struct {
	name          string
	resources     []*DecktetCard
	capital       []*DecktetCard
	palace        []*DecktetCard
	discard       []*DecktetCard
	capitalClaims map[*DecktetCard][]*DecktetCard
	palaceClaims  map[*DecktetCard][]*DecktetCard
}

func NewAdamanPlayer() *AdamanPlayer {
	p := new(AdamanPlayer)
	p.resources = make([]*DecktetCard, 0, 5)
	p.capital = make([]*DecktetCard, 0, 5)
	p.palace = make([]*DecktetCard, 0, 5)
	p.discard = make([]*DecktetCard, 0)

	p.capitalClaims = make(map[*DecktetCard][]*DecktetCard)
	p.palaceClaims = make(map[*DecktetCard][]*DecktetCard)
	return p
}

func (p *AdamanPlayer) AddCard(c gaga.Card) {
	dc := c.(*DecktetCard)
	if len(p.resources) < 5 {
		p.addResource(dc)
	} else if len(p.capital) < 5 {
		p.capital = append(p.capital, dc)
	}
	//return errors.New("shouldn't have drawn a card!")
}

func (p *AdamanPlayer) addResource(card *DecktetCard) {
	if isPerson(card) {
		p.palace = append(p.palace, card)
	} else {
		p.resources = append(p.resources, card)
	}
}

func (p *AdamanPlayer) String() string {
	pal := fmt.Sprintf("\nPalace:\n%v\n", printRow(p.palace))
	c := fmt.Sprintf("Capital:\n%v\n", printRow(p.capital))
	r := fmt.Sprintf("Resources:\n%v\n", printRow(p.resources))

	return pal + c + r
}

func (p *AdamanPlayer) dealAll(d *gaga.Deck) {
	for len(p.capital) != 5 || len(p.resources) != 5 {
		d.Deal(p)
		//fmt.Printf("left in (shuffled) deck: %v\n", len(d.Shuffled))
	}
}

func printRow(row []*DecktetCard) string {
	var s string
	for _, card := range row {
		if isPerson(card) {
			s += fmt.Sprintf("*%v  ", ShortPrintCard(card))
		} else {
			s += fmt.Sprintf(" %v  ", ShortPrintCard(card))
		}
	}
	return s
}

func isPerson(card *DecktetCard) bool {
	for _, cat := range card.Cats() {
		if cat == Person {
			return true
		}
	}
	return false
}

// func evalutate possible claim
// target card. list of cards, can it be claimed, and what's the overage
func evalClaim(target *DecktetCard, cards []*DecktetCard) int {
	// list should already match suits
	var targetV, listV int
	targetV = rankToInt(target)
	for _, c := range cards {
		listV += rankToInt(c)
	}
	return listV - targetV
}

// func find combinations
// target card, a big list of cards, figure out all combinations that could apply
// first all cards that match at least one suit
// find all possible combinations (or is it permutations?) of sublist that matches suit,
// then call our evaluate function
// return the "best" / most efficient
func findCombos(target *DecktetCard, cards []*DecktetCard) (claim []*DecktetCard) {
	var matching []*DecktetCard
	for _, c := range cards { // only consider cards that match a suit
		if SuitMatch(target, c) {
			matching = append(matching, c)
		}
	}

	combos := gaga.CardCombinations(toCardSlice(matching))

	var best []*DecktetCard
	var lowscore int = 100
	for _, c := range combos {
		//fmt.Println(c)
		dc := toDecktetSlice(c)
		score := evalClaim(target, dc)
		//fmt.Println(score)
		if score < 0 {
			continue
		} else if score < lowscore {
			best = dc
			lowscore = score
		}
	}

	return best
}

// func check targets
// given the gamestate
// find combos and evaluate for all targets
// store the overages into a map of some kind
func (p *AdamanPlayer) checkTargets() {
	for _, c := range p.capital {
		p.capitalClaims[c] = findCombos(c, p.resources)
	}

	for _, c := range p.palace {
		p.palaceClaims[c] = findCombos(c, p.resources)
	}

	//fmt.Println("Capital Claims:\n", p.capitalClaims)
	//fmt.Println("Palace Claims:\n", p.palaceClaims)
}

// func decide
// take the map from check targets
// pick the best card to claim
func (p *AdamanPlayer) decideEfficient() bool {
	var target *DecktetCard
	var claim []*DecktetCard
	var lowscore int = 100

	p.checkTargets()
	for k, v := range p.palaceClaims {
		score := evalClaim(k, v)
		if score < lowscore && score >= 0 {
			target = k
			claim = v
			lowscore = score
		}
	}
	for k, v := range p.capitalClaims {
		score := evalClaim(k, v)
		if score < lowscore && score >= 0 {
			target = k
			claim = v
			lowscore = score
		}
	}

	if target == nil {
		return false
	} else {
		fmt.Printf("Decided: claim %v with %v for %v overpayment.\n", ShortPrintCard(target), ShortPrint(claim), lowscore)
		p.claim(target, claim)
		return true
	}
}

func (p *AdamanPlayer) pop(c *DecktetCard, ps *[]*DecktetCard) bool {
	s := *ps
	for i, v := range s {
		if c == v {
			s = append(s[:i], s[i+1:]...)
			return true
		}
	}
	return false
}

func (p *AdamanPlayer) claim(target *DecktetCard, claim []*DecktetCard) {
	// remove the target from capital/palace row
	for i, v := range p.palace {
		if target == v {
			p.palace = append(p.palace[:i], p.palace[i+1:]...)
			delete(p.palaceClaims, target) // clean map
			break
		}
	}

	for i, v := range p.capital {
		if target == v {
			p.capital = append(p.capital[:i], p.capital[i+1:]...)
			delete(p.capitalClaims, target)
			break
		}
	}

	// remove claim cards from resource row
	for _, c := range claim {
		for i, v := range p.resources {
			if c == v {
				p.resources = append(p.resources[:i], p.resources[i+1:]...)
				break
			}
		}
	}

	// discard all used cards
	// make/add a count of how many cards have been claimed
	if isPerson(target) {
		p.discard = append(p.discard, target)
	}

}

func (p *AdamanPlayer) isGameOver() (result string) {
	if len(p.palace) > 5 {
		fmt.Println("palace overfull")
		return "total loss"
	} else if len(p.discard) == 11 {
		fmt.Println("all people captured")
		return "win"
	} else {
		return ""
	}
}

func (p *AdamanPlayer) Play(deck *gaga.Deck) int {
	var result string
	var round int
	for result == "" {
		round++
		fmt.Printf("Round %v!\n", round)
		if len(deck.Shuffled) > 0 {
			p.dealAll(deck)
		}
		fmt.Println(p)
		//fmt.Println(countSuits(deck.Shuffled, true))
		//fmt.Println(countSuits(deck.Shuffled, false))
		result = p.isGameOver()
		if !p.decideEfficient() {
			result = "loss"
			fmt.Println("no more decisions possible")
		}
	}

	fmt.Println("Final table", p)
	fmt.Println("Captured: ", ShortPrint(p.discard))
	return p.score(result)
}

func (p *AdamanPlayer) score(result string) int {
	var totalP, totalR int

	for _, c := range p.discard {
		totalP += rankToInt(c)
	}

	for _, c := range p.resources {
		totalR += rankToInt(c)
	}

	switch result {
	case "total loss":
		return 0
	case "loss":
		return totalP
	case "win":
		return totalP + totalR
	default:
		return -1
	}
}

func main() {
	player := NewAdamanPlayer()
	deck := NewDecktet(BasicDeck)

	deck.Shuffle(1)
	fmt.Println(countSuits(deck.Shuffled, true))
	fmt.Println(countSuits(deck.Shuffled, false))

	score := player.Play(deck)
	fmt.Println(score)

	//fmt.Println(evalClaim(player.capital[0], player.resources))
	//fmt.Println(findCombos(player.capital[0], player.resources))
	//player.decideEfficient()

	/*
		fmt.Println("combos of 3")
		fmt.Println(gaga.Combination(3))
		fmt.Println("combos of 4")
		fmt.Println(gaga.Combination(4))
		fmt.Println("combos of 5")
		fmt.Println(gaga.Combination(5))

		combos := gaga.CardCombinations(toCardSlice(player.resources))

		for _, c := range combos {
			fmt.Println(ShortPrint(toDecktetSlice(c)))
		}
	*/
}

func toCardSlice(dc []*DecktetCard) (c []gaga.Card) {
	c = make([]gaga.Card, len(dc))
	for i := range dc {
		c[i] = dc[i]
	}
	return c
}
func toDecktetSlice(c []gaga.Card) (dc []*DecktetCard) {
	dc = make([]*DecktetCard, len(c))
	for i := range c {
		dc[i] = c[i].(*DecktetCard)
	}
	return dc
}

func rankToInt(c *DecktetCard) int {
	r := c.Rank()
	if r == Crown {
		return 10
	} else {
		return int(r)
	}
}

func countSuits(cards []gaga.Card, onlyPersons bool) map[string]int {
	m := make(map[string]int)
	for _, g := range cards {
		c := g.(*DecktetCard)
		if isPerson(c) || !onlyPersons {
			for _, s := range c.Suits() {
				m[string(s)] += rankToInt(c)
			}
		}
	}
	return m
}
<commit_msg>moved adaman to package, various main functions moved outside package<commit_after>package adaman

import (
	"fmt"

	"github.com/frenata/gaga"
	. "github.com/frenata/gaga/decktet"
)

type AdamanPlayer struct {
	name          string
	resources     []*DecktetCard
	capital       []*DecktetCard
	palace        []*DecktetCard
	discard       []*DecktetCard
	capitalClaims map[*DecktetCard][]*DecktetCard
	palaceClaims  map[*DecktetCard][]*DecktetCard
}

func NewAdamanPlayer() *AdamanPlayer {
	p := new(AdamanPlayer)
	p.resources = make([]*DecktetCard, 0, 5)
	p.capital = make([]*DecktetCard, 0, 5)
	p.palace = make([]*DecktetCard, 0, 5)
	p.discard = make([]*DecktetCard, 0)

	p.capitalClaims = make(map[*DecktetCard][]*DecktetCard)
	p.palaceClaims = make(map[*DecktetCard][]*DecktetCard)
	return p
}

func (p *AdamanPlayer) AddCard(c gaga.Card) {
	dc := c.(*DecktetCard)
	if len(p.resources) < 5 {
		p.addResource(dc)
	} else if len(p.capital) < 5 {
		p.capital = append(p.capital, dc)
	}
	//return errors.New("shouldn't have drawn a card!")
}

func (p *AdamanPlayer) addResource(card *DecktetCard) {
	if isPerson(card) {
		p.palace = append(p.palace, card)
	} else {
		p.resources = append(p.resources, card)
	}
}

func (p *AdamanPlayer) String() string {
	pal := fmt.Sprintf("\nPalace:\n%v\n", printRow(p.palace))
	c := fmt.Sprintf("Capital:\n%v\n", printRow(p.capital))
	r := fmt.Sprintf("Resources:\n%v\n", printRow(p.resources))

	return pal + c + r
}

func (p *AdamanPlayer) dealAll(d *gaga.Deck) {
	for len(p.capital) != 5 || len(p.resources) != 5 {
		if len(d.Shuffled) == 0 {
			return
		}
		d.Deal(p)
		//fmt.Printf("left in (shuffled) deck: %v\n", len(d.Shuffled))
	}
}

func printRow(row []*DecktetCard) string {
	var s string
	for _, card := range row {
		if isPerson(card) {
			s += fmt.Sprintf("*%v  ", ShortPrintCard(card))
		} else {
			s += fmt.Sprintf(" %v  ", ShortPrintCard(card))
		}
	}
	return s
}

func isPerson(card *DecktetCard) bool {
	for _, cat := range card.Cats() {
		if cat == Person {
			return true
		}
	}
	return false
}

// func evalutate possible claim
// target card. list of cards, can it be claimed, and what's the overage
func evalClaim(target *DecktetCard, cards []*DecktetCard) int {
	// list should already match suits
	var targetV, listV int
	targetV = rankToInt(target)
	for _, c := range cards {
		listV += rankToInt(c)
	}
	return listV - targetV
}

// func find combinations
// target card, a big list of cards, figure out all combinations that could apply
// first all cards that match at least one suit
// find all possible combinations (or is it permutations?) of sublist that matches suit,
// then call our evaluate function
// return the "best" / most efficient
func findCombos(target *DecktetCard, cards []*DecktetCard) (claim []*DecktetCard) {
	var matching []*DecktetCard
	for _, c := range cards { // only consider cards that match a suit
		if SuitMatch(target, c) {
			matching = append(matching, c)
		}
	}

	combos := gaga.CardCombinations(toCardSlice(matching))

	var best []*DecktetCard
	var lowscore int = 100
	for _, c := range combos {
		//fmt.Println(c)
		dc := toDecktetSlice(c)
		score := evalClaim(target, dc)
		//fmt.Println(score)
		if score < 0 {
			continue
		} else if score < lowscore {
			best = dc
			lowscore = score
		}
	}

	return best
}

// func check targets
// given the gamestate
// find combos and evaluate for all targets
// store the overages into a map of some kind
func (p *AdamanPlayer) checkTargets() {
	for _, c := range p.capital {
		p.capitalClaims[c] = findCombos(c, p.resources)
	}

	for _, c := range p.palace {
		p.palaceClaims[c] = findCombos(c, p.resources)
	}

	//fmt.Println("Capital Claims:\n", p.capitalClaims)
	//fmt.Println("Palace Claims:\n", p.palaceClaims)
}

// func decide
// take the map from check targets
// pick the best card to claim
func (p *AdamanPlayer) decideEfficient() bool {
	var target *DecktetCard
	var claim []*DecktetCard
	var lowscore int = 100

	p.checkTargets()
	for k, v := range p.palaceClaims {
		score := evalClaim(k, v)
		if score < lowscore && score >= 0 {
			target = k
			claim = v
			lowscore = score
		}
	}
	for k, v := range p.capitalClaims {
		score := evalClaim(k, v)
		if score < lowscore && score >= 0 {
			target = k
			claim = v
			lowscore = score
		}
	}

	if target == nil {
		return false
	} else {
		fmt.Printf("Decided: claim %v with %v for %v overpayment.\n", ShortPrintCard(target), ShortPrint(claim), lowscore)
		p.claim(target, claim)
		return true
	}
}

func (p *AdamanPlayer) pop(c *DecktetCard, ps *[]*DecktetCard) bool {
	s := *ps
	for i, v := range s {
		if c == v {
			s = append(s[:i], s[i+1:]...)
			return true
		}
	}
	return false
}

func (p *AdamanPlayer) claim(target *DecktetCard, claim []*DecktetCard) {
	// remove the target from capital/palace row
	for i, v := range p.palace {
		if target == v {
			p.palace = append(p.palace[:i], p.palace[i+1:]...)
			delete(p.palaceClaims, target) // clean map
			break
		}
	}

	for i, v := range p.capital {
		if target == v {
			p.capital = append(p.capital[:i], p.capital[i+1:]...)
			delete(p.capitalClaims, target)
			break
		}
	}

	// remove claim cards from resource row
	for _, c := range claim {
		for i, v := range p.resources {
			if c == v {
				p.resources = append(p.resources[:i], p.resources[i+1:]...)
				break
			}
		}
	}

	// discard all used cards
	// make/add a count of how many cards have been claimed
	if isPerson(target) {
		p.discard = append(p.discard, target)
	}

}

func (p *AdamanPlayer) isGameOver() (result string) {
	if len(p.palace) > 5 {
		fmt.Println("palace overfull")
		return "total loss"
	} else if len(p.discard) == 11 {
		fmt.Println("all people captured")
		return "win"
	} else {
		return ""
	}
}

func (p *AdamanPlayer) Play(deck *gaga.Deck) int {
	var result string
	var round int
	for result == "" {
		round++
		fmt.Printf("Round %v!\n", round)
		if len(deck.Shuffled) > 0 {
			p.dealAll(deck)
		}
		fmt.Println(p)
		//fmt.Println(countSuits(deck.Shuffled, true))
		//fmt.Println(countSuits(deck.Shuffled, false))
		result = p.isGameOver()
		if result != "" {
			break
		}
		if !p.decideEfficient() {
			result = "loss"
			fmt.Println("no more decisions possible")
		}
	}

	fmt.Println("Final table", p)
	fmt.Println("Captured: ", ShortPrint(p.discard))
	return p.score(result)
}

func (p *AdamanPlayer) score(result string) int {
	var totalP, totalR int

	for _, c := range p.discard {
		totalP += rankToInt(c)
	}

	for _, c := range p.resources {
		totalR += rankToInt(c)
	}

	switch result {
	case "total loss":
		return 0
	case "loss":
		return totalP
	case "win":
		return totalP + totalR
	default:
		panic("bad end condition")
		return -1
	}
}

func toCardSlice(dc []*DecktetCard) (c []gaga.Card) {
	c = make([]gaga.Card, len(dc))
	for i := range dc {
		c[i] = dc[i]
	}
	return c
}
func toDecktetSlice(c []gaga.Card) (dc []*DecktetCard) {
	dc = make([]*DecktetCard, len(c))
	for i := range c {
		dc[i] = c[i].(*DecktetCard)
	}
	return dc
}

func rankToInt(c *DecktetCard) int {
	r := c.Rank()
	if r == Crown {
		return 10
	} else {
		return int(r)
	}
}

func countSuits(cards []gaga.Card, onlyPersons bool) map[string]int {
	m := make(map[string]int)
	for _, g := range cards {
		c := g.(*DecktetCard)
		if isPerson(c) || !onlyPersons {
			for _, s := range c.Suits() {
				m[string(s)] += rankToInt(c)
			}
		}
	}
	return m
}
<|endoftext|>


<commit_before>package zcl

import "fmt"

// Pos represents a single position in a source file, by addressing the
// start byte of a unicode character encoded in UTF-8.
//
// Pos is generally used only in the context of a Range, which then defines
// which source file the position is within.
type Pos struct {
	// Line is the source code line where this position points. Lines are
	// counted starting at 1 and incremented for each newline character
	// encountered.
	Line int

	// Column is the source code column where this position points, in
	// unicode characters, with counting starting at 1.
	//
	// Column counts characters as they appear visually, so for example a
	// latin letter with a combining diacritic mark counts as one character.
	// This is intended for rendering visual markers against source code in
	// contexts where these diacritics would be rendered in a single character
	// cell. Technically speaking, Column is counting grapheme clusters as
	// used in unicode normalization.
	Column int

	// Byte is the byte offset into the file where the indicated character
	// begins. This is a zero-based offset to the first byte of the first
	// UTF-8 codepoint sequence in the character, and thus gives a position
	// that can be resolved _without_ awareness of Unicode characters.
	Byte int
}

// Range represents a span of characters between two positions in a source
// file.
//
// This struct is usually used by value in types that represent AST nodes,
// but by pointer in types that refer to the positions of other objects,
// such as in diagnostics.
type Range struct {
	// Filename is the name of the file into which this range's positions
	// point.
	Filename string

	// Start and End represent the bounds of this range. Start is inclusive
	// and End is exclusive.
	Start, End Pos
}

// RangeBetween returns a new range that spans from the beginning of the
// start range to the end of the end range.
//
// The result is meaningless if the two ranges do not belong to the same
// source file or if the end range appears before the start range.
func RangeBetween(start, end Range) Range {
	return Range{
		Filename: start.Filename,
		Start:    start.Start,
		End:      end.End,
	}
}

// ContainsOffset returns true if and only if the given byte offset is within
// the receiving Range.
func (r Range) ContainsOffset(offset int) bool {
	return offset >= r.Start.Byte && offset < r.End.Byte
}

// Ptr returns a pointer to a copy of the receiver. This is a convenience when
// ranges in places where pointers are required, such as in Diagnostic, but
// the range in question is returned from a method. Go would otherwise not
// allow one to take the address of a function call.
func (r Range) Ptr() *Range {
	return &r
}

// String returns a compact string representation of the receiver.
// Callers should generally prefer to present a range more visually,
// e.g. via markers directly on the relevant portion of source code.
func (r *Range) String() string {
	if r.Start.Line == r.End.Line {
		return fmt.Sprintf(
			"%s:%d,%d-%d",
			r.Filename,
			r.Start.Line, r.Start.Column,
			r.End.Column,
		)
	} else {
		return fmt.Sprintf(
			"%s:%d,%d-%d,%d",
			r.Filename,
			r.Start.Line, r.Start.Column,
			r.End.Line, r.End.Column,
		)
	}
}
<commit_msg>zcl: Range.String should not be a pointer method<commit_after>package zcl

import "fmt"

// Pos represents a single position in a source file, by addressing the
// start byte of a unicode character encoded in UTF-8.
//
// Pos is generally used only in the context of a Range, which then defines
// which source file the position is within.
type Pos struct {
	// Line is the source code line where this position points. Lines are
	// counted starting at 1 and incremented for each newline character
	// encountered.
	Line int

	// Column is the source code column where this position points, in
	// unicode characters, with counting starting at 1.
	//
	// Column counts characters as they appear visually, so for example a
	// latin letter with a combining diacritic mark counts as one character.
	// This is intended for rendering visual markers against source code in
	// contexts where these diacritics would be rendered in a single character
	// cell. Technically speaking, Column is counting grapheme clusters as
	// used in unicode normalization.
	Column int

	// Byte is the byte offset into the file where the indicated character
	// begins. This is a zero-based offset to the first byte of the first
	// UTF-8 codepoint sequence in the character, and thus gives a position
	// that can be resolved _without_ awareness of Unicode characters.
	Byte int
}

// Range represents a span of characters between two positions in a source
// file.
//
// This struct is usually used by value in types that represent AST nodes,
// but by pointer in types that refer to the positions of other objects,
// such as in diagnostics.
type Range struct {
	// Filename is the name of the file into which this range's positions
	// point.
	Filename string

	// Start and End represent the bounds of this range. Start is inclusive
	// and End is exclusive.
	Start, End Pos
}

// RangeBetween returns a new range that spans from the beginning of the
// start range to the end of the end range.
//
// The result is meaningless if the two ranges do not belong to the same
// source file or if the end range appears before the start range.
func RangeBetween(start, end Range) Range {
	return Range{
		Filename: start.Filename,
		Start:    start.Start,
		End:      end.End,
	}
}

// ContainsOffset returns true if and only if the given byte offset is within
// the receiving Range.
func (r Range) ContainsOffset(offset int) bool {
	return offset >= r.Start.Byte && offset < r.End.Byte
}

// Ptr returns a pointer to a copy of the receiver. This is a convenience when
// ranges in places where pointers are required, such as in Diagnostic, but
// the range in question is returned from a method. Go would otherwise not
// allow one to take the address of a function call.
func (r Range) Ptr() *Range {
	return &r
}

// String returns a compact string representation of the receiver.
// Callers should generally prefer to present a range more visually,
// e.g. via markers directly on the relevant portion of source code.
func (r Range) String() string {
	if r.Start.Line == r.End.Line {
		return fmt.Sprintf(
			"%s:%d,%d-%d",
			r.Filename,
			r.Start.Line, r.Start.Column,
			r.End.Column,
		)
	} else {
		return fmt.Sprintf(
			"%s:%d,%d-%d,%d",
			r.Filename,
			r.Start.Line, r.Start.Column,
			r.End.Line, r.End.Column,
		)
	}
}
<|endoftext|>


<commit_before>package zk

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/funkygao/golib/gofmt"
)

type zkTimestamp int64

func (this zkTimestamp) Time() time.Time {
	return time.Unix(int64(this)/int64(1000), 0)
}

type zkData struct {
	data  []byte
	mtime zkTimestamp
}

type ConsumerMeta struct {
	Group          string
	Online         bool
	Topic          string
	PartitionId    string
	Mtime          zkTimestamp
	ConsumerOffset int64
	ProducerOffset int64
	Lag            int64
	ConsumerZnode  *ConsumerZnode
}

type ControllerMeta struct {
	Broker *BrokerZnode
	Mtime  zkTimestamp
	Epoch  string
}

func (c *ControllerMeta) String() string {
	return fmt.Sprintf("%s epoch:%s/%s %s", c.Broker.Id, c.Epoch,
		gofmt.PrettySince(c.Mtime.Time()), c.Broker.String())
}

type TopicZnode struct {
	Name       string           `json:-`
	Version    int              `json:"version"`
	Partitions map[string][]int `json:"partitions"` // {partitionId: replicas}
}

type ConsumerZnode struct {
	Id           string         `json:-`
	Version      int            `json:"version"`
	Subscription map[string]int `json:"subscription"` // topic:count
	Pattern      string         `json:"pattern"`
	Timestamp    string         `json:"timestamp"`
}

func newConsumerZnode(id string) *ConsumerZnode {
	return &ConsumerZnode{Id: id}
}

func (c *ConsumerZnode) from(zkData []byte) {
	if err := json.Unmarshal(zkData, c); err != nil {
		panic(string(zkData) + ": " + err.Error())
	}
}

func (c *ConsumerZnode) Host() string {
	// consumerId: $group_$hostname-$timestamp-$uuid
	dashN := 0
	var lo, hi int
	for hi = len(c.Id) - 1; hi >= 0; hi-- {
		if c.Id[hi] == '-' {
			dashN++
			if dashN == 2 {
				break
			}
		}
	}

	for lo = hi; c.Id[lo] != '_'; lo-- {
	}

	return c.Id[lo+1 : hi]
}

func (c *ConsumerZnode) Topics() []string {
	r := make([]string, 0, len(c.Subscription))
	for topic, _ := range c.Subscription {
		r = append(r, topic)
	}
	return r
}

func (c *ConsumerZnode) Uptime() time.Time {
	return TimestampToTime(c.Timestamp)
}

func (c *ConsumerZnode) String() string {
	return fmt.Sprintf("host:%s, sub:%+v, uptime:%s",
		c.Host(), c.Subscription, gofmt.PrettySince(c.Uptime()))
}

type BrokerZnode struct {
	Id        string   `json:-`
	JmxPort   int      `json:"jmx_port"`
	Timestamp string   `json:"timestamp"`
	Endpoints []string `json:"endpoints"`
	Host      string   `json:"host"`
	Port      int      `json:"port"`
	Version   int      `json:"version"`
}

func newBrokerZnode(id string) *BrokerZnode {
	return &BrokerZnode{Id: id}
}

func (b BrokerZnode) String() string {
	return fmt.Sprintf("%s:%d ver:%d uptime:%s",
		b.Host, b.Port,
		b.Version,
		gofmt.PrettySince(b.Uptime()))
}

func (b *BrokerZnode) Uptime() time.Time {
	return TimestampToTime(b.Timestamp)
}

func (b *BrokerZnode) from(zkData []byte) {
	if err := json.Unmarshal(zkData, b); err != nil {
		panic(string(zkData) + ": " + err.Error())
	}
}

func (b *BrokerZnode) Addr() string {
	return fmt.Sprintf("%s:%d", b.Host, b.Port)
}
<commit_msg>unfortunately, golang consumer id fmt is not compatible with java<commit_after>package zk

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/funkygao/golib/gofmt"
)

type zkTimestamp int64

func (this zkTimestamp) Time() time.Time {
	return time.Unix(int64(this)/int64(1000), 0)
}

type zkData struct {
	data  []byte
	mtime zkTimestamp
}

type ConsumerMeta struct {
	Group          string
	Online         bool
	Topic          string
	PartitionId    string
	Mtime          zkTimestamp
	ConsumerOffset int64
	ProducerOffset int64
	Lag            int64
	ConsumerZnode  *ConsumerZnode
}

type ControllerMeta struct {
	Broker *BrokerZnode
	Mtime  zkTimestamp
	Epoch  string
}

func (c *ControllerMeta) String() string {
	return fmt.Sprintf("%s epoch:%s/%s %s", c.Broker.Id, c.Epoch,
		gofmt.PrettySince(c.Mtime.Time()), c.Broker.String())
}

type TopicZnode struct {
	Name       string           `json:-`
	Version    int              `json:"version"`
	Partitions map[string][]int `json:"partitions"` // {partitionId: replicas}
}

type ConsumerZnode struct {
	Id           string         `json:-`
	Version      int            `json:"version"`
	Subscription map[string]int `json:"subscription"` // topic:count
	Pattern      string         `json:"pattern"`
	Timestamp    string         `json:"timestamp"`
}

func newConsumerZnode(id string) *ConsumerZnode {
	return &ConsumerZnode{Id: id}
}

func (c *ConsumerZnode) from(zkData []byte) {
	if err := json.Unmarshal(zkData, c); err != nil {
		panic(string(zkData) + ": " + err.Error())
	}
}

func (c *ConsumerZnode) Host() string {
	// consumer id
	// java: $group_$hostname-$timestamp-$uuidSignificantBits
	//   go: $hostname:$uuidFull

	if strings.Contains(c.Id, ":") {
		return c.hostInGoClientFmt()
	}

	dashN := 0
	var lo, hi int
	for hi = len(c.Id) - 1; hi >= 0; hi-- {
		if c.Id[hi] == '-' {
			dashN++
			if dashN == 2 {
				break
			}
		}
	}

	for lo = hi; c.Id[lo] != '_'; lo-- {
	}

	return c.Id[lo+1 : hi]
}

func (c *ConsumerZnode) hostInGoClientFmt() string {
	p := strings.SplitN(c.Id, ":", 2)
	return p[0]
}

func (c *ConsumerZnode) Topics() []string {
	r := make([]string, 0, len(c.Subscription))
	for topic, _ := range c.Subscription {
		r = append(r, topic)
	}
	return r
}

func (c *ConsumerZnode) Uptime() time.Time {
	return TimestampToTime(c.Timestamp)
}

func (c *ConsumerZnode) String() string {
	return fmt.Sprintf("host:%s, sub:%+v, uptime:%s",
		c.Host(), c.Subscription, gofmt.PrettySince(c.Uptime()))
}

type BrokerZnode struct {
	Id        string   `json:-`
	JmxPort   int      `json:"jmx_port"`
	Timestamp string   `json:"timestamp"`
	Endpoints []string `json:"endpoints"`
	Host      string   `json:"host"`
	Port      int      `json:"port"`
	Version   int      `json:"version"`
}

func newBrokerZnode(id string) *BrokerZnode {
	return &BrokerZnode{Id: id}
}

func (b BrokerZnode) String() string {
	return fmt.Sprintf("%s:%d ver:%d uptime:%s",
		b.Host, b.Port,
		b.Version,
		gofmt.PrettySince(b.Uptime()))
}

func (b *BrokerZnode) Uptime() time.Time {
	return TimestampToTime(b.Timestamp)
}

func (b *BrokerZnode) from(zkData []byte) {
	if err := json.Unmarshal(zkData, b); err != nil {
		panic(string(zkData) + ": " + err.Error())
	}
}

func (b *BrokerZnode) Addr() string {
	return fmt.Sprintf("%s:%d", b.Host, b.Port)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
)

func main() {
	args := os.Args
	if len(args) < 2 {
		fmt.Println("Please provide at least one service tag.")
		fmt.Printf("Usage: %s 12345", args[0])
	} else {
		content, err := searchServiceTags(args[1:])
		if err != nil {
			fmt.Printf("Sorry, an error occurred.")
		} else {
			if len(args) == 2 {
				getWarrantyInformation(content, false)
			} else {
				getWarrantyInformation(content, true)
			}
		}
	}
}
<commit_msg>Update main.go<commit_after>package main

import (
	"fmt"
	"os"
)

func main() {
	args := os.Args
	if len(args) < 2 {
		fmt.Println("Please provide at least one service tag.")
		//k33bz: Updated to match documentation
		fmt.Printf("Usage: %s 1A2B3C4", args[0])
	} else {
		content, err := searchServiceTags(args[1:])
		if err != nil {
			fmt.Printf("Sorry, an error occurred.")
		} else {
			if len(args) == 2 {
				getWarrantyInformation(content, false)
			} else {
				getWarrantyInformation(content, true)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"unicode"

	"github.com/charlievieth/buildutil"
	"golang.org/x/sync/singleflight"
)

type CompLintRequest struct {
	Filename string `json:"filename"`
}

type CompileError struct {
	Row     int    `json:"row"`
	Col     int    `json:"col"`
	File    string `json:"file"`
	Message string `json:"message"`
}

type CompLintReport struct {
	Filename      string         `json:"filename"`
	TopLevelError string         `json:"top_level_error,omitempty"`
	CmdError      string         `json:"cmd_error,omitempty"`
	Errors        []CompileError `json:"errors,omitempty"`
}

var compRe = regexp.MustCompile(`(?m)^(?P<file>[^:#]+\.go)\:(?P<row>\d+)\:(?:(?P<col>\d+)\:)?\s*(?P<msg>.+)$`)

func (r *CompLintReport) ParseErrors(dirname string, out []byte) {
	const (
		FileIndex     = 1
		RowIndex      = 2
		ColIndex      = 3
		MsgIndex      = 4
		SubmatchCount = 5
	)
	out = bytes.TrimSpace(out)

	first := out
	if n := bytes.IndexByte(out, '\n'); n > 0 {
		first = bytes.TrimRightFunc(out[:n], unicode.IsSpace)
	}
	if len(first) != 0 && first[0] != '#' {
		r.TopLevelError = string(first)
	}

	matches := compRe.FindAllSubmatch(out, -1)
	for _, m := range matches {
		if len(m) != SubmatchCount {
			continue
		}
		row, _ := strconv.Atoi(string(m[RowIndex]))
		col, _ := strconv.Atoi(string(m[ColIndex]))
		file := string(m[FileIndex])
		if !filepath.IsAbs(file) {
			file = filepath.Join(dirname, file)
		}
		r.Errors = append(r.Errors, CompileError{
			Row:     row,
			Col:     col,
			File:    file,
			Message: string(m[MsgIndex]),
		})
	}
}

// Send compile output to /dev/null if a directory exists with the same
// name as the default output binary
func (c *CompLintRequest) BuildOutput() []string {
	if runtime.GOOS != "windows" {
		dir := filepath.Dir(c.Filename)
		output := filepath.Join(dir, filepath.Base(dir))
		if isDir(output) {
			return []string{"-o", os.DevNull}
		}
	}
	return nil
}

func (c *CompLintRequest) Compile(src []byte) *CompLintReport {
	pkgname, _ := buildutil.ReadPackageName(c.Filename, src)

	ctxt, _ := buildutil.MatchContext(nil, c.Filename, src)
	if ctxt == nil {
		ctxt = &build.Default
	}

	var args []string
	switch {
	case strings.HasSuffix(c.Filename, "_test.go"):
		args = []string{"test", "-c", "-i", "-o", os.DevNull}
	case pkgname == "main":
		args = []string{"build", "-i"}
		if extra := c.BuildOutput(); len(extra) != 0 {
			args = append(args, extra...)
		}
	default:
		args = []string{"install", "-i"}
	}

	cmd := buildutil.GoCommand(ctxt, "go", args...)
	cmd.Dir = filepath.Dir(c.Filename)

	out, err := cmd.CombinedOutput()
	r := &CompLintReport{
		Filename: c.Filename,
	}
	if err != nil {
		r.CmdError = err.Error()
		r.ParseErrors(cmd.Dir, out)
	}
	return r
}

var compLintGroup singleflight.Group

func (c *CompLintRequest) Call() (interface{}, string) {
	src, err := ioutil.ReadFile(c.Filename)
	if err != nil {
		return &CompLintReport{CmdError: err.Error()}, err.Error()
	}
	key := string(src)
	v, _, _ := compLintGroup.Do(key, func() (interface{}, error) {
		return c.Compile(src), nil
	})
	r, ok := v.(*CompLintReport)
	if !ok {
		return nil, fmt.Sprintf("complint: invalid return type: %T", v)
	}
	return r, ""
}

func init() {
	registry.Register("comp_lint", func(_ *Broker) Caller {
		return &CompLintRequest{}
	})
}
<commit_msg>m_comp_lint: remove deprecated "-i" build flag<commit_after>package main

import (
	"bytes"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"unicode"

	"github.com/charlievieth/buildutil"
	"golang.org/x/sync/singleflight"
)

type CompLintRequest struct {
	Filename string `json:"filename"`
}

type CompileError struct {
	Row     int    `json:"row"`
	Col     int    `json:"col"`
	File    string `json:"file"`
	Message string `json:"message"`
}

type CompLintReport struct {
	Filename      string         `json:"filename"`
	TopLevelError string         `json:"top_level_error,omitempty"`
	CmdError      string         `json:"cmd_error,omitempty"`
	Errors        []CompileError `json:"errors,omitempty"`
}

var compRe = regexp.MustCompile(`(?m)^(?P<file>[^:#]+\.go)\:(?P<row>\d+)\:(?:(?P<col>\d+)\:)?\s*(?P<msg>.+)$`)

func (r *CompLintReport) ParseErrors(dirname string, out []byte) {
	const (
		FileIndex     = 1
		RowIndex      = 2
		ColIndex      = 3
		MsgIndex      = 4
		SubmatchCount = 5
	)
	out = bytes.TrimSpace(out)

	first := out
	if n := bytes.IndexByte(out, '\n'); n > 0 {
		first = bytes.TrimRightFunc(out[:n], unicode.IsSpace)
	}
	if len(first) != 0 && first[0] != '#' {
		r.TopLevelError = string(first)
	}

	matches := compRe.FindAllSubmatch(out, -1)
	for _, m := range matches {
		if len(m) != SubmatchCount {
			continue
		}
		row, _ := strconv.Atoi(string(m[RowIndex]))
		col, _ := strconv.Atoi(string(m[ColIndex]))
		file := string(m[FileIndex])
		if !filepath.IsAbs(file) {
			file = filepath.Join(dirname, file)
		}
		r.Errors = append(r.Errors, CompileError{
			Row:     row,
			Col:     col,
			File:    file,
			Message: string(m[MsgIndex]),
		})
	}
}

// Send compile output to /dev/null if a directory exists with the same
// name as the default output binary
func (c *CompLintRequest) BuildOutput() []string {
	if runtime.GOOS != "windows" {
		dir := filepath.Dir(c.Filename)
		output := filepath.Join(dir, filepath.Base(dir))
		if isDir(output) {
			return []string{"-o", os.DevNull}
		}
	}
	return nil
}

func (c *CompLintRequest) Compile(src []byte) *CompLintReport {
	pkgname, _ := buildutil.ReadPackageName(c.Filename, src)

	ctxt, _ := buildutil.MatchContext(nil, c.Filename, src)
	if ctxt == nil {
		ctxt = &build.Default
	}

	var args []string
	switch {
	case strings.HasSuffix(c.Filename, "_test.go"):
		args = []string{"test", "-c", "-o", os.DevNull}
	case pkgname == "main":
		args = []string{"build"}
		if extra := c.BuildOutput(); len(extra) != 0 {
			args = append(args, extra...)
		}
	default:
		args = []string{"install"}
	}

	cmd := buildutil.GoCommand(ctxt, "go", args...)
	cmd.Dir = filepath.Dir(c.Filename)

	out, err := cmd.CombinedOutput()
	r := &CompLintReport{
		Filename: c.Filename,
	}
	if err != nil {
		r.CmdError = err.Error()
		r.ParseErrors(cmd.Dir, out)
	}
	return r
}

var compLintGroup singleflight.Group

func (c *CompLintRequest) Call() (interface{}, string) {
	src, err := ioutil.ReadFile(c.Filename)
	if err != nil {
		return &CompLintReport{CmdError: err.Error()}, err.Error()
	}
	key := string(src)
	v, _, _ := compLintGroup.Do(key, func() (interface{}, error) {
		return c.Compile(src), nil
	})
	r, ok := v.(*CompLintReport)
	if !ok {
		return nil, fmt.Sprintf("complint: invalid return type: %T", v)
	}
	return r, ""
}

func init() {
	registry.Register("comp_lint", func(_ *Broker) Caller {
		return &CompLintRequest{}
	})
}
<|endoftext|>


<commit_before>package db

import (
	"context"
	"fmt"
	"os"

	log "github.com/sirupsen/logrus"

	"github.com/argoproj/argo-cd/v2/common"
	appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
	"github.com/argoproj/argo-cd/v2/util/gpg"
)

// Validates a single GnuPG key and returns the key's ID
func validatePGPKey(keyData string) (*appsv1.GnuPGPublicKey, error) {
	f, err := os.CreateTemp("", "gpg-public-key")
	if err != nil {
		return nil, err
	}
	defer os.Remove(f.Name())

	err = os.WriteFile(f.Name(), []byte(keyData), 0600)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	parsed, err := gpg.ValidatePGPKeys(f.Name())
	if err != nil {
		return nil, err
	}

	// Each key/value pair in the config map must exactly contain one public key, with the (short) GPG key ID as key
	if len(parsed) != 1 {
		return nil, fmt.Errorf("More than one key found in input data")
	}

	var retKey *appsv1.GnuPGPublicKey = nil
	// Is there a better way to get the first element from a map without knowing its key?
	for _, k := range parsed {
		retKey = k
		break
	}
	if retKey != nil {
		retKey.KeyData = keyData
		return retKey, nil
	} else {
		return nil, fmt.Errorf("Could not find the GPG key")
	}
}

// ListConfiguredGPGPublicKeys returns a list of all configured GPG public keys from the ConfigMap
func (db *db) ListConfiguredGPGPublicKeys(ctx context.Context) (map[string]*appsv1.GnuPGPublicKey, error) {
	log.Debugf("Loading PGP public keys from config map")
	result := make(map[string]*appsv1.GnuPGPublicKey)
	keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName)
	if err != nil {
		return nil, err
	}

	// We have to verify all PGP keys in the ConfigMap to be valid keys before. To do so,
	// we write each single one out to a temporary file and validate them through gpg.
	// This is not optimal, but the executil from argo-pkg does not support writing to
	// stdin of the forked process. So for now, we must live with that.
	for k, p := range keysCM.Data {
		if expectedKeyID := gpg.KeyID(k); expectedKeyID != "" {
			parsedKey, err := validatePGPKey(p)
			if err != nil {
				return nil, fmt.Errorf("Could not parse GPG key for entry '%s': %s", expectedKeyID, err.Error())
			}
			if expectedKeyID != parsedKey.KeyID {
				return nil, fmt.Errorf("Key parsed for entry with key ID '%s' had different key ID '%s'", expectedKeyID, parsedKey.KeyID)
			}
			result[parsedKey.KeyID] = parsedKey
		} else {
			return nil, fmt.Errorf("Found entry with key '%s' in ConfigMap, but this is not a valid PGP key ID", k)
		}
	}

	return result, nil
}

// AddGPGPublicKey adds one or more public keys to the configuration
func (db *db) AddGPGPublicKey(ctx context.Context, keyData string) (map[string]*appsv1.GnuPGPublicKey, []string, error) {
	result := make(map[string]*appsv1.GnuPGPublicKey)
	skipped := make([]string, 0)

	keys, err := gpg.ValidatePGPKeysFromString(keyData)
	if err != nil {
		return nil, nil, err
	}

	keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName)
	if err != nil {
		return nil, nil, err
	}

	if keysCM.Data == nil {
		keysCM.Data = make(map[string]string)
	}

	for kid, key := range keys {
		if _, ok := keysCM.Data[kid]; ok {
			skipped = append(skipped, kid)
			log.Debugf("Not adding incoming key with kid=%s because it is configured already", kid)
		} else {
			result[kid] = key
			keysCM.Data[kid] = key.KeyData
			log.Debugf("Adding incoming key with kid=%s to database", kid)
		}
	}

	err = db.settingsMgr.SaveGPGPublicKeyData(ctx, keysCM.Data)
	if err != nil {
		return nil, nil, err
	}

	return result, skipped, nil
}

// DeleteGPGPublicKey deletes a GPG public key from the configuration
func (db *db) DeleteGPGPublicKey(ctx context.Context, keyID string) error {
	keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName)
	if err != nil {
		return err
	}

	if keysCM.Data == nil {
		return fmt.Errorf("No such key configured: %s", keyID)
	}

	if _, ok := keysCM.Data[keyID]; !ok {
		return fmt.Errorf("No such key configured: %s", keyID)
	}

	delete(keysCM.Data, keyID)

	err = db.settingsMgr.SaveGPGPublicKeyData(ctx, keysCM.Data)
	return err
}
<commit_msg>fix: Add logic to handle for f.Close() for util/db (#9963) (#10127)<commit_after>package db

import (
	"context"
	"fmt"
	"os"

	log "github.com/sirupsen/logrus"

	"github.com/argoproj/argo-cd/v2/common"
	appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
	"github.com/argoproj/argo-cd/v2/util/gpg"
)

// Validates a single GnuPG key and returns the key's ID
func validatePGPKey(keyData string) (*appsv1.GnuPGPublicKey, error) {
	f, err := os.CreateTemp("", "gpg-public-key")
	if err != nil {
		return nil, err
	}
	defer os.Remove(f.Name())

	err = os.WriteFile(f.Name(), []byte(keyData), 0600)
	if err != nil {
		return nil, err
	}
	defer func() {
		err = f.Close()
		if err != nil {
			log.Errorf("error closing file %q: %v", f.Name(), err)
		}
	}()

	parsed, err := gpg.ValidatePGPKeys(f.Name())
	if err != nil {
		return nil, err
	}

	// Each key/value pair in the config map must exactly contain one public key, with the (short) GPG key ID as key
	if len(parsed) != 1 {
		return nil, fmt.Errorf("More than one key found in input data")
	}

	var retKey *appsv1.GnuPGPublicKey = nil
	// Is there a better way to get the first element from a map without knowing its key?
	for _, k := range parsed {
		retKey = k
		break
	}
	if retKey != nil {
		retKey.KeyData = keyData
		return retKey, nil
	} else {
		return nil, fmt.Errorf("Could not find the GPG key")
	}
}

// ListConfiguredGPGPublicKeys returns a list of all configured GPG public keys from the ConfigMap
func (db *db) ListConfiguredGPGPublicKeys(ctx context.Context) (map[string]*appsv1.GnuPGPublicKey, error) {
	log.Debugf("Loading PGP public keys from config map")
	result := make(map[string]*appsv1.GnuPGPublicKey)
	keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName)
	if err != nil {
		return nil, err
	}

	// We have to verify all PGP keys in the ConfigMap to be valid keys before. To do so,
	// we write each single one out to a temporary file and validate them through gpg.
	// This is not optimal, but the executil from argo-pkg does not support writing to
	// stdin of the forked process. So for now, we must live with that.
	for k, p := range keysCM.Data {
		if expectedKeyID := gpg.KeyID(k); expectedKeyID != "" {
			parsedKey, err := validatePGPKey(p)
			if err != nil {
				return nil, fmt.Errorf("Could not parse GPG key for entry '%s': %s", expectedKeyID, err.Error())
			}
			if expectedKeyID != parsedKey.KeyID {
				return nil, fmt.Errorf("Key parsed for entry with key ID '%s' had different key ID '%s'", expectedKeyID, parsedKey.KeyID)
			}
			result[parsedKey.KeyID] = parsedKey
		} else {
			return nil, fmt.Errorf("Found entry with key '%s' in ConfigMap, but this is not a valid PGP key ID", k)
		}
	}

	return result, nil
}

// AddGPGPublicKey adds one or more public keys to the configuration
func (db *db) AddGPGPublicKey(ctx context.Context, keyData string) (map[string]*appsv1.GnuPGPublicKey, []string, error) {
	result := make(map[string]*appsv1.GnuPGPublicKey)
	skipped := make([]string, 0)

	keys, err := gpg.ValidatePGPKeysFromString(keyData)
	if err != nil {
		return nil, nil, err
	}

	keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName)
	if err != nil {
		return nil, nil, err
	}

	if keysCM.Data == nil {
		keysCM.Data = make(map[string]string)
	}

	for kid, key := range keys {
		if _, ok := keysCM.Data[kid]; ok {
			skipped = append(skipped, kid)
			log.Debugf("Not adding incoming key with kid=%s because it is configured already", kid)
		} else {
			result[kid] = key
			keysCM.Data[kid] = key.KeyData
			log.Debugf("Adding incoming key with kid=%s to database", kid)
		}
	}

	err = db.settingsMgr.SaveGPGPublicKeyData(ctx, keysCM.Data)
	if err != nil {
		return nil, nil, err
	}

	return result, skipped, nil
}

// DeleteGPGPublicKey deletes a GPG public key from the configuration
func (db *db) DeleteGPGPublicKey(ctx context.Context, keyID string) error {
	keysCM, err := db.settingsMgr.GetConfigMapByName(common.ArgoCDGPGKeysConfigMapName)
	if err != nil {
		return err
	}

	if keysCM.Data == nil {
		return fmt.Errorf("No such key configured: %s", keyID)
	}

	if _, ok := keysCM.Data[keyID]; !ok {
		return fmt.Errorf("No such key configured: %s", keyID)
	}

	delete(keysCM.Data, keyID)

	err = db.settingsMgr.SaveGPGPublicKeyData(ctx, keysCM.Data)
	return err
}
<|endoftext|>


<commit_before>package assert

func isPrime(value int) bool {
	if value <= 3 {
		return value >= 2
	}
	if value%2 == 0 || value%3 == 0 {
		return false
	}
	for i := 5; i*i <= value; i += 6 {
		if value%i == 0 || value%(i+2) == 0 {
			return false
		}
	}
	return true
}
<commit_msg>fixed infinite loop on 32-bit machines (e.g. playground server)<commit_after>package assert

func isPrime(value int) bool {
	if value <= 3 {
		return value >= 2
	}
	if value%2 == 0 || value%3 == 0 {
		return false
	}
	v64 := int64(value)
	for i := int64(5); i*i <= v64; i += 6 {
		if v64%i == 0 || v64%(i+2) == 0 {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/lxc/lxd/shared/logger"
)

/*
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <errno.h>
#include <fcntl.h>
#include <linux/kcmp.h>
#include <linux/types.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <syscall.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <sys/ptrace.h>

#include "../shared/netutils/netns_getifaddrs.c"
#include "include/compiler.h"
#include "include/lxd_seccomp.h"
#include "include/memory_utils.h"

__ro_after_init bool netnsid_aware = false;
__ro_after_init bool uevent_aware = false;
__ro_after_init int seccomp_notify_aware = 0;
__ro_after_init char errbuf[4096];

extern int can_inject_uevent(const char *uevent, size_t len);

static int netns_set_nsid(int fd)
{
	__do_close_prot_errno int sockfd = -EBADF;
	int ret;
	char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
		 NLMSG_ALIGN(sizeof(struct rtgenmsg)) +
		 NLMSG_ALIGN(1024)];
	struct nlmsghdr *hdr;
	struct rtgenmsg *msg;
	__s32 ns_id = -1;
	__u32 netns_fd = fd;

	sockfd = netlink_open(NETLINK_ROUTE);
	if (sockfd < 0)
		return -1;

	memset(buf, 0, sizeof(buf));
	hdr = (struct nlmsghdr *)buf;
	msg = (struct rtgenmsg *)NLMSG_DATA(hdr);

	hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*msg));
	hdr->nlmsg_type = RTM_NEWNSID;
	hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
	hdr->nlmsg_pid = 0;
	hdr->nlmsg_seq = RTM_NEWNSID;
	msg->rtgen_family = AF_UNSPEC;

	addattr(hdr, 1024, __LXC_NETNSA_FD, &netns_fd, sizeof(netns_fd));
	addattr(hdr, 1024, __LXC_NETNSA_NSID, &ns_id, sizeof(ns_id));

	ret = netlink_transaction(sockfd, hdr, hdr);
	if (ret < 0)
		return -1;

	return 0;
}

void is_netnsid_aware(int *hostnetns_fd, int *newnetns_fd)
{
	__do_close_prot_errno int sock_fd = -EBADF;
	int netnsid, ret;

	*hostnetns_fd = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC);
	if (*hostnetns_fd < 0) {
		(void)sprintf(errbuf, "%s", "Failed to preserve host network namespace");
		return;
	}

	ret = unshare(CLONE_NEWNET);
	if (ret < 0) {
		(void)sprintf(errbuf, "%s", "Failed to unshare network namespace");
		return;
	}

	*newnetns_fd = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC);
	if (*newnetns_fd < 0) {
		(void)sprintf(errbuf, "%s", "Failed to preserve new network namespace");
		return;
	}

	ret = netns_set_nsid(*hostnetns_fd);
	if (ret < 0) {
		(void)sprintf(errbuf, "%s", "failed to set network namespace identifier");
		return;
	}

	netnsid = netns_get_nsid(*hostnetns_fd);
	if (netnsid < 0) {
		(void)sprintf(errbuf, "%s", "Failed to get network namespace identifier");
		return;
	}

	sock_fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
	if (sock_fd < 0) {
		(void)sprintf(errbuf, "%s", "Failed to open netlink routing socket");
		return;
	}

	ret = setsockopt(sock_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &(int){1}, sizeof(int));
	if (ret < 0) {
		// NETLINK_GET_STRICT_CHK isn't supported
		return;
	}

	// NETLINK_GET_STRICT_CHK is supported
	netnsid_aware = true;
}

static void is_uevent_aware(void)
{
	if (can_inject_uevent("dummy", 6) < 0)
		return;

	uevent_aware = true;
}

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

static int user_trap_syscall(int nr, unsigned int flags)
{
	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
			offsetof(struct seccomp_data, nr)),
		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, nr, 0, 1),
		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_USER_NOTIF),
		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
	};

	struct sock_fprog prog = {
		.len = (unsigned short)ARRAY_SIZE(filter),
		.filter = filter,
	};

	return syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, flags, &prog);
}

// The ifdef can be safely ignored. We don't work on a kernel that old.
static int filecmp(pid_t pid1, pid_t pid2, int fd1, int fd2)
{
#ifdef __NR_kcmp
	return syscall(__NR_kcmp, pid1, pid2, KCMP_FILE, fd1, fd2);
#else
	errno = ENOSYS;
	return -1;
#endif
}

__noreturn static void __do_user_notification_continue(void)
{
	pid_t pid;
	int ret;
	int status, listener;
	struct seccomp_notif req = {};
	struct seccomp_notif_resp resp = {};
	struct pollfd pollfd;

	listener = user_trap_syscall(__NR_dup, SECCOMP_FILTER_FLAG_NEW_LISTENER);
	if (listener < 0)
		exit(1);

	pid = fork();
	if (pid < 0)
		exit(1);

	if (pid == 0) {
		int dup_fd, pipe_fds[2];
		pid_t self;

		// Don't bother cleaning up. On child exit all of those
		// will be closed anyway.
		ret = pipe(pipe_fds);
		if (ret < 0)
			exit(1);

		// O_CLOEXEC doesn't matter as we're in the child and we're
		// not going to exec.
		dup_fd = dup(pipe_fds[0]);
		if (dup_fd < 0)
			exit(1);

		self = getpid();

		ret = filecmp(self, self, pipe_fds[0], dup_fd);
		if (ret)
			exit(2);

		exit(0);
	}

	pollfd.fd = listener;
	pollfd.events = POLLIN | POLLOUT;

	ret = poll(&pollfd, 1, 5000);
	if (ret <= 0)
		goto cleanup_sigkill;

	if (!(pollfd.revents & POLLIN))
		goto cleanup_sigkill;

	ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req);
	if (ret)
		goto cleanup_sigkill;

	pollfd.fd = listener;
	pollfd.events = POLLIN | POLLOUT;

	ret = poll(&pollfd, 1, 5000);
	if (ret <= 0)
		goto cleanup_sigkill;

	if (!(pollfd.revents & POLLOUT))
		goto cleanup_sigkill;

	if (req.data.nr != __NR_dup)
		goto cleanup_sigkill;

	resp.id = req.id;
	resp.flags |= SECCOMP_USER_NOTIF_FLAG_CONTINUE;
	ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp);
	resp.error = -EPERM;
	resp.flags = 0;
	if (ret) {
		ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp);
		goto cleanup_sigkill;
	}

cleanup_wait:
	ret = waitpid(pid, &status, 0);
	if ((ret != pid) || !WIFEXITED(status) || WEXITSTATUS(status))
		exit(1);
	exit(0);

cleanup_sigkill:
	kill(pid, SIGKILL);
	goto cleanup_wait;
}

static void is_user_notification_continue_aware(void)
{
	int ret, status;
	pid_t pid;

	pid = fork();
	if (pid < 0)
		return;

	if (pid == 0) {
		__do_user_notification_continue();
		// Should not be reached.
		_exit(EXIT_FAILURE);
	}

	ret = waitpid(pid, &status, 0);
	if ((ret == pid) && WIFEXITED(status) && !WEXITSTATUS(status))
		seccomp_notify_aware = 2;
}

static void is_seccomp_notify_aware(void)
{
	__u32 action[] = { SECCOMP_RET_USER_NOTIF };

	if (syscall(__NR_seccomp, SECCOMP_GET_ACTION_AVAIL, 0, &action[0]) == 0) {
		seccomp_notify_aware = 1;
		is_user_notification_continue_aware();
	}

}

void checkfeature(void)
{
	__do_close_prot_errno int hostnetns_fd = -EBADF, newnetns_fd = -EBADF;

	is_netnsid_aware(&hostnetns_fd, &newnetns_fd);
	is_uevent_aware();
	is_seccomp_notify_aware();

	if (setns(hostnetns_fd, CLONE_NEWNET) < 0)
		(void)sprintf(errbuf, "%s", "Failed to attach to host network namespace");

}

static bool is_empty_string(char *s)
{
	return (errbuf[0] == '\0');
}
*/
import "C"

func CanUseNetnsGetifaddrs() bool {
	if !bool(C.is_empty_string(&C.errbuf[0])) {
		logger.Debugf("%s", C.GoString(&C.errbuf[0]))
	}

	return bool(C.netnsid_aware)
}

func CanUseUeventInjection() bool {
	return bool(C.uevent_aware)
}

func CanUseSeccompListener() bool {
	return bool(C.seccomp_notify_aware > 0)
}

func CanUseSeccompListenerContinue() bool {
	return bool(C.seccomp_notify_aware == 2)
}
<commit_msg>lxd/main_checkfeature: s/exit()/_exit()/g<commit_after>package main

import (
	"github.com/lxc/lxd/shared/logger"
)

/*
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1
#endif
#include <errno.h>
#include <fcntl.h>
#include <linux/kcmp.h>
#include <linux/types.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <syscall.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <sys/ptrace.h>

#include "../shared/netutils/netns_getifaddrs.c"
#include "include/compiler.h"
#include "include/lxd_seccomp.h"
#include "include/memory_utils.h"

__ro_after_init bool netnsid_aware = false;
__ro_after_init bool uevent_aware = false;
__ro_after_init int seccomp_notify_aware = 0;
__ro_after_init char errbuf[4096];

extern int can_inject_uevent(const char *uevent, size_t len);

static int netns_set_nsid(int fd)
{
	__do_close_prot_errno int sockfd = -EBADF;
	int ret;
	char buf[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
		 NLMSG_ALIGN(sizeof(struct rtgenmsg)) +
		 NLMSG_ALIGN(1024)];
	struct nlmsghdr *hdr;
	struct rtgenmsg *msg;
	__s32 ns_id = -1;
	__u32 netns_fd = fd;

	sockfd = netlink_open(NETLINK_ROUTE);
	if (sockfd < 0)
		return -1;

	memset(buf, 0, sizeof(buf));
	hdr = (struct nlmsghdr *)buf;
	msg = (struct rtgenmsg *)NLMSG_DATA(hdr);

	hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*msg));
	hdr->nlmsg_type = RTM_NEWNSID;
	hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
	hdr->nlmsg_pid = 0;
	hdr->nlmsg_seq = RTM_NEWNSID;
	msg->rtgen_family = AF_UNSPEC;

	addattr(hdr, 1024, __LXC_NETNSA_FD, &netns_fd, sizeof(netns_fd));
	addattr(hdr, 1024, __LXC_NETNSA_NSID, &ns_id, sizeof(ns_id));

	ret = netlink_transaction(sockfd, hdr, hdr);
	if (ret < 0)
		return -1;

	return 0;
}

void is_netnsid_aware(int *hostnetns_fd, int *newnetns_fd)
{
	__do_close_prot_errno int sock_fd = -EBADF;
	int netnsid, ret;

	*hostnetns_fd = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC);
	if (*hostnetns_fd < 0) {
		(void)sprintf(errbuf, "%s", "Failed to preserve host network namespace");
		return;
	}

	ret = unshare(CLONE_NEWNET);
	if (ret < 0) {
		(void)sprintf(errbuf, "%s", "Failed to unshare network namespace");
		return;
	}

	*newnetns_fd = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC);
	if (*newnetns_fd < 0) {
		(void)sprintf(errbuf, "%s", "Failed to preserve new network namespace");
		return;
	}

	ret = netns_set_nsid(*hostnetns_fd);
	if (ret < 0) {
		(void)sprintf(errbuf, "%s", "failed to set network namespace identifier");
		return;
	}

	netnsid = netns_get_nsid(*hostnetns_fd);
	if (netnsid < 0) {
		(void)sprintf(errbuf, "%s", "Failed to get network namespace identifier");
		return;
	}

	sock_fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
	if (sock_fd < 0) {
		(void)sprintf(errbuf, "%s", "Failed to open netlink routing socket");
		return;
	}

	ret = setsockopt(sock_fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &(int){1}, sizeof(int));
	if (ret < 0) {
		// NETLINK_GET_STRICT_CHK isn't supported
		return;
	}

	// NETLINK_GET_STRICT_CHK is supported
	netnsid_aware = true;
}

static void is_uevent_aware(void)
{
	if (can_inject_uevent("dummy", 6) < 0)
		return;

	uevent_aware = true;
}

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

static int user_trap_syscall(int nr, unsigned int flags)
{
	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
			offsetof(struct seccomp_data, nr)),
		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, nr, 0, 1),
		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_USER_NOTIF),
		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
	};

	struct sock_fprog prog = {
		.len = (unsigned short)ARRAY_SIZE(filter),
		.filter = filter,
	};

	return syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, flags, &prog);
}

// The ifdef can be safely ignored. We don't work on a kernel that old.
static int filecmp(pid_t pid1, pid_t pid2, int fd1, int fd2)
{
#ifdef __NR_kcmp
	return syscall(__NR_kcmp, pid1, pid2, KCMP_FILE, fd1, fd2);
#else
	errno = ENOSYS;
	return -1;
#endif
}

__noreturn static void __do_user_notification_continue(void)
{
	pid_t pid;
	int ret;
	int status, listener;
	struct seccomp_notif req = {};
	struct seccomp_notif_resp resp = {};
	struct pollfd pollfd;

	listener = user_trap_syscall(__NR_dup, SECCOMP_FILTER_FLAG_NEW_LISTENER);
	if (listener < 0)
		_exit(EXIT_FAILURE);

	pid = fork();
	if (pid < 0)
		_exit(EXIT_FAILURE);

	if (pid == 0) {
		int dup_fd, pipe_fds[2];
		pid_t self;

		// Don't bother cleaning up. On child exit all of those
		// will be closed anyway.
		ret = pipe(pipe_fds);
		if (ret < 0)
			_exit(EXIT_FAILURE);

		// O_CLOEXEC doesn't matter as we're in the child and we're
		// not going to exec.
		dup_fd = dup(pipe_fds[0]);
		if (dup_fd < 0)
			_exit(EXIT_FAILURE);

		self = getpid();

		ret = filecmp(self, self, pipe_fds[0], dup_fd);
		if (ret)
			_exit(EXIT_FAILURE);

		_exit(EXIT_SUCCESS);
	}

	pollfd.fd = listener;
	pollfd.events = POLLIN | POLLOUT;

	ret = poll(&pollfd, 1, 5000);
	if (ret <= 0)
		goto cleanup_sigkill;

	if (!(pollfd.revents & POLLIN))
		goto cleanup_sigkill;

	ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req);
	if (ret)
		goto cleanup_sigkill;

	pollfd.fd = listener;
	pollfd.events = POLLIN | POLLOUT;

	ret = poll(&pollfd, 1, 5000);
	if (ret <= 0)
		goto cleanup_sigkill;

	if (!(pollfd.revents & POLLOUT))
		goto cleanup_sigkill;

	if (req.data.nr != __NR_dup)
		goto cleanup_sigkill;

	resp.id = req.id;
	resp.flags |= SECCOMP_USER_NOTIF_FLAG_CONTINUE;
	ret = ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp);
	resp.error = -EPERM;
	resp.flags = 0;
	if (ret) {
		ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp);
		goto cleanup_sigkill;
	}

cleanup_wait:
	ret = waitpid(pid, &status, 0);
	if ((ret != pid) || !WIFEXITED(status) || WEXITSTATUS(status))
		_exit(EXIT_FAILURE);
	_exit(EXIT_SUCCESS);

cleanup_sigkill:
	kill(pid, SIGKILL);
	goto cleanup_wait;
}

static void is_user_notification_continue_aware(void)
{
	int ret, status;
	pid_t pid;

	pid = fork();
	if (pid < 0)
		return;

	if (pid == 0) {
		__do_user_notification_continue();
		// Should not be reached.
		_exit(EXIT_FAILURE);
	}

	ret = waitpid(pid, &status, 0);
	if ((ret == pid) && WIFEXITED(status) && !WEXITSTATUS(status))
		seccomp_notify_aware = 2;
}

static void is_seccomp_notify_aware(void)
{
	__u32 action[] = { SECCOMP_RET_USER_NOTIF };

	if (syscall(__NR_seccomp, SECCOMP_GET_ACTION_AVAIL, 0, &action[0]) == 0) {
		seccomp_notify_aware = 1;
		is_user_notification_continue_aware();
	}

}

void checkfeature(void)
{
	__do_close_prot_errno int hostnetns_fd = -EBADF, newnetns_fd = -EBADF;

	is_netnsid_aware(&hostnetns_fd, &newnetns_fd);
	is_uevent_aware();
	is_seccomp_notify_aware();

	if (setns(hostnetns_fd, CLONE_NEWNET) < 0)
		(void)sprintf(errbuf, "%s", "Failed to attach to host network namespace");

}

static bool is_empty_string(char *s)
{
	return (errbuf[0] == '\0');
}
*/
import "C"

func CanUseNetnsGetifaddrs() bool {
	if !bool(C.is_empty_string(&C.errbuf[0])) {
		logger.Debugf("%s", C.GoString(&C.errbuf[0]))
	}

	return bool(C.netnsid_aware)
}

func CanUseUeventInjection() bool {
	return bool(C.uevent_aware)
}

func CanUseSeccompListener() bool {
	return bool(C.seccomp_notify_aware > 0)
}

func CanUseSeccompListenerContinue() bool {
	return bool(C.seccomp_notify_aware == 2)
}
<|endoftext|>


<commit_before>/*

database.go

Database handling
Implements gorm ORM: https://github.com/jinzhu/gorm

*/

package main

import (
	"github.com/jinzhu/gorm"
	_ "github.com/mattn/go-sqlite3"
)

func OpenDbConn() *gorm.DB {
	db, err := gorm.Open("sqlite3", "./database/data.db")
	if err != nil {
		panic("Failed to connect to db")
	}

	return db
}
<commit_msg>Updated database.go to use the database package instead of main<commit_after>/*

database.go

Database handling
Implements gorm ORM: https://github.com/jinzhu/gorm

*/

package database

import (
	"github.com/jinzhu/gorm"
	_ "github.com/mattn/go-sqlite3"
)

func OpenDbConn() *gorm.DB {
	db, err := gorm.Open("sqlite3", "./database/data.db")
	if err != nil {
		panic("Failed to connect to db")
	}

	return db
}
<|endoftext|>


<commit_before>package database

import (
	"github.com/danjac/podbaby/models"
	"github.com/jmoiron/sqlx"
)

// PodcastDB manages DB queries to podcasts
type PodcastDB interface {
	SelectSubscribed(int64, int64) (*models.PodcastList, error)
	SelectByChannelID(int64, int64, int64) (*models.PodcastList, error)
	SelectBookmarked(int64, int64) (*models.PodcastList, error)
	SelectPlayed(int64, int64) (*models.PodcastList, error)
	Search(string, int64) ([]models.Podcast, error)
	Create(*models.Podcast) error
}

type defaultPodcastDBImpl struct {
	*sqlx.DB
}

func (db *defaultPodcastDBImpl) Search(query string, userID int64) ([]models.Podcast, error) {

	sql := `SELECT p.id, p.title, p.enclosure_url, p.description,
    p.channel_id, p.pub_date, c.title AS name, c.image,
    EXISTS(SELECT id FROM bookmarks WHERE podcast_id=p.id AND user_id=$1)
      AS is_bookmarked,
    EXISTS(SELECT id FROM subscriptions WHERE channel_id=p.channel_id AND user_id=$1)
      AS is_subscribed
    FROM podcasts p, plainto_tsquery($2) as q, channels c
    WHERE (p.tsv @@ q) AND p.channel_id = c.id
    ORDER BY ts_rank_cd(p.tsv, plainto_tsquery($2)) DESC LIMIT 20`

	var podcasts []models.Podcast
	return podcasts, db.Select(&podcasts, sql, userID, query)

}

func (db *defaultPodcastDBImpl) SelectPlayed(userID, page int64) (*models.PodcastList, error) {

	sql := `SELECT COUNT(DISTINCT(p.id)) FROM podcasts p
    JOIN plays pl ON pl.podcast_id = p.id
    WHERE pl.user_id=$1`

	var numRows int64

	if err := db.QueryRow(sql, userID).Scan(&numRows); err != nil {
		return nil, err
	}

	result := &models.PodcastList{
		Page: models.NewPage(page, numRows),
	}

	sql = `SELECT p.id, p.title, p.enclosure_url, p.description,
    p.channel_id, p.pub_date, c.title AS name, c.image,
    EXISTS(SELECT id FROM bookmarks WHERE podcast_id=p.id AND user_id=$1)
      AS is_bookmarked,
    EXISTS(SELECT id FROM subscriptions WHERE channel_id=p.channel_id AND user_id=$1)
      AS is_subscribed
    FROM podcasts p
    JOIN plays pl ON pl.podcast_id = p.id
    JOIN channels c ON c.id = p.channel_id
    WHERE pl.user_id=$1
    GROUP BY p.id, c.title, c.image, pl.created_at
    ORDER BY pl.created_at DESC
    OFFSET $2 LIMIT $3`

	err := db.Select(
		&result.Podcasts,
		sql,
		userID,
		result.Page.Offset,
		result.Page.PageSize)
	return result, err

}

func (db *defaultPodcastDBImpl) SelectSubscribed(userID, page int64) (*models.PodcastList, error) {

	sql := `SELECT COUNT(DISTINCT(p.id)) FROM podcasts p
  JOIN subscriptions s ON s.channel_id = p.channel_id
  WHERE s.user_id=$1`

	var numRows int64

	if err := db.QueryRow(sql, userID).Scan(&numRows); err != nil {
		return nil, err
	}

	result := &models.PodcastList{
		Page: models.NewPage(page, numRows),
	}

	sql = `SELECT p.id, p.title, p.enclosure_url, p.description,
    p.channel_id, c.title AS name, c.image, p.pub_date,
    EXISTS(SELECT id FROM bookmarks WHERE podcast_id=p.id AND user_id=$1)
      AS is_bookmarked,
    1 AS is_subscribed
    FROM podcasts p
    JOIN subscriptions s ON s.channel_id = p.channel_id
    JOIN channels c ON c.id = p.channel_id
    WHERE s.user_id=$1
    GROUP BY p.id, p.title, c.image, c.title
    ORDER BY pub_date DESC
    OFFSET $2 LIMIT $3`
	err := db.Select(
		&result.Podcasts,
		sql,
		userID,
		result.Page.Offset,
		result.Page.PageSize)
	return result, err
}

func (db *defaultPodcastDBImpl) SelectBookmarked(userID, page int64) (*models.PodcastList, error) {

	sql := `SELECT COUNT(DISTINCT(p.id)) FROM podcasts p
  JOIN bookmarks b ON b.podcast_id = p.id
  WHERE b.user_id=$1`

	var numRows int64

	if err := db.QueryRow(sql, userID).Scan(&numRows); err != nil {
		return nil, err
	}

	result := &models.PodcastList{
		Page: models.NewPage(page, numRows),
	}

	sql = `SELECT p.id, p.title, p.enclosure_url, p.description,
    p.channel_id, c.title AS name, c.image, p.pub_date,
    EXISTS(SELECT id FROM subscriptions WHERE channel_id=p.channel_id AND user_id=$1)
      AS is_subscribed,
    1 AS is_bookmarked
    FROM podcasts p
    JOIN channels c ON c.id = p.channel_id
    JOIN bookmarks b ON b.podcast_id = p.id
    WHERE b.user_id=$1
    GROUP BY p.id, p.title, c.title, c.image
    ORDER BY p.title
    OFFSET $2 LIMIT $3`

	err := db.Select(
		&result.Podcasts,
		sql,
		userID,
		result.Page.Offset,
		result.Page.PageSize)
	return result, err
}

func (db *defaultPodcastDBImpl) SelectByChannelID(channelID, userID, page int64) (*models.PodcastList, error) {

	sql := `SELECT COUNT(id) FROM podcasts WHERE channel_id=$1`

	var numRows int64

	if err := db.QueryRow(sql, channelID).Scan(&numRows); err != nil {
		return nil, err
	}

	result := &models.PodcastList{
		Page: models.NewPage(page, numRows),
	}

	sql = `SELECT id, title, enclosure_url, description, pub_date,
    EXISTS(SELECT id FROM bookmarks WHERE podcast_id=podcasts.id AND user_id=$1)
      AS is_bookmarked
    FROM podcasts
    WHERE channel_id=$2
    ORDER BY pub_date DESC
    OFFSET $3 LIMIT $4`

	err := db.Select(
		&result.Podcasts,
		sql,
		userID,
		channelID,
		result.Page.Offset,
		result.Page.PageSize)
	return result, err
}

func (db *defaultPodcastDBImpl) Create(pc *models.Podcast) error {
	query, args, err := sqlx.Named("SELECT insert_podcast(:channel_id, :title, :description, :enclosure_url, :pub_date)", pc)
	if err != nil {
		return err
	}
	return db.QueryRow(db.Rebind(query), args...).Scan(&pc.ID)
}
<commit_msg>Ordering bookmarks<commit_after>package database

import (
	"github.com/danjac/podbaby/models"
	"github.com/jmoiron/sqlx"
)

// PodcastDB manages DB queries to podcasts
type PodcastDB interface {
	SelectSubscribed(int64, int64) (*models.PodcastList, error)
	SelectByChannelID(int64, int64, int64) (*models.PodcastList, error)
	SelectBookmarked(int64, int64) (*models.PodcastList, error)
	SelectPlayed(int64, int64) (*models.PodcastList, error)
	Search(string, int64) ([]models.Podcast, error)
	Create(*models.Podcast) error
}

type defaultPodcastDBImpl struct {
	*sqlx.DB
}

func (db *defaultPodcastDBImpl) Search(query string, userID int64) ([]models.Podcast, error) {

	sql := `SELECT p.id, p.title, p.enclosure_url, p.description,
    p.channel_id, p.pub_date, c.title AS name, c.image,
    EXISTS(SELECT id FROM bookmarks WHERE podcast_id=p.id AND user_id=$1)
      AS is_bookmarked,
    EXISTS(SELECT id FROM subscriptions WHERE channel_id=p.channel_id AND user_id=$1)
      AS is_subscribed
    FROM podcasts p, plainto_tsquery($2) as q, channels c
    WHERE (p.tsv @@ q) AND p.channel_id = c.id
    ORDER BY ts_rank_cd(p.tsv, plainto_tsquery($2)) DESC LIMIT 20`

	var podcasts []models.Podcast
	return podcasts, db.Select(&podcasts, sql, userID, query)

}

func (db *defaultPodcastDBImpl) SelectPlayed(userID, page int64) (*models.PodcastList, error) {

	sql := `SELECT COUNT(DISTINCT(p.id)) FROM podcasts p
    JOIN plays pl ON pl.podcast_id = p.id
    WHERE pl.user_id=$1`

	var numRows int64

	if err := db.QueryRow(sql, userID).Scan(&numRows); err != nil {
		return nil, err
	}

	result := &models.PodcastList{
		Page: models.NewPage(page, numRows),
	}

	sql = `SELECT p.id, p.title, p.enclosure_url, p.description,
    p.channel_id, p.pub_date, c.title AS name, c.image,
    EXISTS(SELECT id FROM bookmarks WHERE podcast_id=p.id AND user_id=$1)
      AS is_bookmarked,
    EXISTS(SELECT id FROM subscriptions WHERE channel_id=p.channel_id AND user_id=$1)
      AS is_subscribed
    FROM podcasts p
    JOIN plays pl ON pl.podcast_id = p.id
    JOIN channels c ON c.id = p.channel_id
    WHERE pl.user_id=$1
    GROUP BY p.id, c.title, c.image, pl.created_at
    ORDER BY pl.created_at DESC
    OFFSET $2 LIMIT $3`

	err := db.Select(
		&result.Podcasts,
		sql,
		userID,
		result.Page.Offset,
		result.Page.PageSize)
	return result, err

}

func (db *defaultPodcastDBImpl) SelectSubscribed(userID, page int64) (*models.PodcastList, error) {

	sql := `SELECT COUNT(DISTINCT(p.id)) FROM podcasts p
  JOIN subscriptions s ON s.channel_id = p.channel_id
  WHERE s.user_id=$1`

	var numRows int64

	if err := db.QueryRow(sql, userID).Scan(&numRows); err != nil {
		return nil, err
	}

	result := &models.PodcastList{
		Page: models.NewPage(page, numRows),
	}

	sql = `SELECT p.id, p.title, p.enclosure_url, p.description,
    p.channel_id, c.title AS name, c.image, p.pub_date,
    EXISTS(SELECT id FROM bookmarks WHERE podcast_id=p.id AND user_id=$1)
      AS is_bookmarked,
    1 AS is_subscribed
    FROM podcasts p
    JOIN subscriptions s ON s.channel_id = p.channel_id
    JOIN channels c ON c.id = p.channel_id
    WHERE s.user_id=$1
    GROUP BY p.id, p.title, c.image, c.title
    ORDER BY pub_date DESC
    OFFSET $2 LIMIT $3`
	err := db.Select(
		&result.Podcasts,
		sql,
		userID,
		result.Page.Offset,
		result.Page.PageSize)
	return result, err
}

func (db *defaultPodcastDBImpl) SelectBookmarked(userID, page int64) (*models.PodcastList, error) {

	sql := `SELECT COUNT(DISTINCT(p.id)) FROM podcasts p
  JOIN bookmarks b ON b.podcast_id = p.id
  WHERE b.user_id=$1`

	var numRows int64

	if err := db.QueryRow(sql, userID).Scan(&numRows); err != nil {
		return nil, err
	}

	result := &models.PodcastList{
		Page: models.NewPage(page, numRows),
	}

	sql = `SELECT p.id, p.title, p.enclosure_url, p.description,
    p.channel_id, c.title AS name, c.image, p.pub_date,
    EXISTS(SELECT id FROM subscriptions WHERE channel_id=p.channel_id AND user_id=$1)
      AS is_subscribed,
    1 AS is_bookmarked
    FROM podcasts p
    JOIN channels c ON c.id = p.channel_id
    JOIN bookmarks b ON b.podcast_id = p.id
    WHERE b.user_id=$1
    GROUP BY p.id, p.title, c.title, c.image
    ORDER BY c.title, p.title
    OFFSET $2 LIMIT $3`

	err := db.Select(
		&result.Podcasts,
		sql,
		userID,
		result.Page.Offset,
		result.Page.PageSize)
	return result, err
}

func (db *defaultPodcastDBImpl) SelectByChannelID(channelID, userID, page int64) (*models.PodcastList, error) {

	sql := `SELECT COUNT(id) FROM podcasts WHERE channel_id=$1`

	var numRows int64

	if err := db.QueryRow(sql, channelID).Scan(&numRows); err != nil {
		return nil, err
	}

	result := &models.PodcastList{
		Page: models.NewPage(page, numRows),
	}

	sql = `SELECT id, title, enclosure_url, description, pub_date,
    EXISTS(SELECT id FROM bookmarks WHERE podcast_id=podcasts.id AND user_id=$1)
      AS is_bookmarked
    FROM podcasts
    WHERE channel_id=$2
    ORDER BY pub_date DESC
    OFFSET $3 LIMIT $4`

	err := db.Select(
		&result.Podcasts,
		sql,
		userID,
		channelID,
		result.Page.Offset,
		result.Page.PageSize)
	return result, err
}

func (db *defaultPodcastDBImpl) Create(pc *models.Podcast) error {
	query, args, err := sqlx.Named("SELECT insert_podcast(:channel_id, :title, :description, :enclosure_url, :pub_date)", pc)
	if err != nil {
		return err
	}
	return db.QueryRow(db.Rebind(query), args...).Scan(&pc.ID)
}
<|endoftext|>


<commit_before>package utils

import (
	"testing"
	"time"
	"github.com/stretchr/testify/assert"
	"github.com/pkg/errors"
)

func TestWaitForSpecificOrError(t *testing.T){

	waitInterval := 100*time.Millisecond
	timeout := 1000*time.Millisecond
	times := 0
	err := WaitForSpecificOrError(func() (bool, error) {
		times += 1
		println("times", times)
		if times == 3 {
			return true, nil
		}
		return false, nil
	}, timeout, waitInterval)
	assert.NoError(t, err)
	assert.Equal(t, 3, times)

	times = 0
	err = WaitForSpecificOrError(func() (bool, error) {
		times += 1
		println("times", times)
		if times == 3 {
			return false, errors.New("error")
		}
		return false, nil
	}, timeout, waitInterval)
	assert.Error(t, err)
	assert.Equal(t, 3, times)

	times = 0
	err = WaitForSpecificOrError(func() (bool, error) {
		times += 1
		println("times", times)
		return false, nil
	}, timeout, waitInterval)
	assert.Error(t, err)
	assert.Equal(t, 10, times)
}<commit_msg>fix error package import.<commit_after>package utils

import (
	"testing"
	"time"
	"github.com/stretchr/testify/assert"
	"errors"
)

func TestWaitForSpecificOrError(t *testing.T){

	waitInterval := 100*time.Millisecond
	timeout := 1000*time.Millisecond
	times := 0
	err := WaitForSpecificOrError(func() (bool, error) {
		times += 1
		println("times", times)
		if times == 3 {
			return true, nil
		}
		return false, nil
	}, timeout, waitInterval)
	assert.NoError(t, err)
	assert.Equal(t, 3, times)

	times = 0
	err = WaitForSpecificOrError(func() (bool, error) {
		times += 1
		println("times", times)
		if times == 3 {
			return false, errors.New("error")
		}
		return false, nil
	}, timeout, waitInterval)
	assert.Error(t, err)
	assert.Equal(t, 3, times)

	times = 0
	err = WaitForSpecificOrError(func() (bool, error) {
		times += 1
		println("times", times)
		return false, nil
	}, timeout, waitInterval)
	assert.Error(t, err)
	assert.Equal(t, 10, times)
}<|endoftext|>


<commit_before>package jantar

import (
	"fmt"
	"io"
	"log"
	"os"
)

// Defines the various logging level
const (
	LogLevelDebug   = iota
	LogLevelInfo    = iota
	LogLevelWarning = iota
	LogLevelError   = iota
	LogLevelFatal   = iota
	LogLevelPanic   = iota
)

const (
	nocolor = 0
	red     = 31
	green   = 32
	yellow  = 33
	blue    = 34
)

// JLogger is a simple Log with some handy functions. Largely inspired by https://github.com/Sirupsen/logrus
type JLogger struct {
	log      *log.Logger
	minLevel uint
	ansiMode bool
}

// JLData is a type describing map that can be passed to Data<Level>f functions for debug output
type JLData map[string]interface{}

func levelToColor(level uint) int {
	switch level {
	case LogLevelInfo:
		return blue
	case LogLevelDebug:
		return green
	case LogLevelWarning:
		return yellow
	case LogLevelError:
		fallthrough
	case LogLevelFatal:
		fallthrough
	case LogLevelPanic:
		return red
	default:
		return nocolor
	}
}

func levelToString(level uint) string {
	switch level {
	case LogLevelInfo:
		return "INFO"
	case LogLevelDebug:
		return "DEBU"
	case LogLevelWarning:
		return "WARN"
	case LogLevelError:
		return "ERRO"
	case LogLevelFatal:
		return "FATA"
	case LogLevelPanic:
		return "PANI"
	default:
		return "Unknown"
	}
}

// NewJLogger creates a new JLogger instance
func NewJLogger(out io.Writer, prefix string, minLevel uint) *JLogger {
	ansiMode := false
	if isTerminal(os.Stderr.Fd()) {
		ansiMode = true
	}

	return &JLogger{log.New(out, prefix, 0), minLevel, ansiMode}
}

// SetMinLevel changes the loggers current minimal logging level
func (l *JLogger) SetMinLevel(minLevel uint) {
	if minLevel < LogLevelInfo || minLevel > LogLevelPanic {
		l.minLevel = LogLevelInfo
	} else {
		l.minLevel = minLevel
	}
}

// Infodf is equivalent to Infof but takes additional data to display
func (l *JLogger) Infodf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelInfo, data, format, v...)
}

// Debugdf is equivalent to Infodf but uses a different logging level
func (l *JLogger) Debugdf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelDebug, data, format, v...)
}

// Warningdf is equivalent to Infodf but uses a different logging level
func (l *JLogger) Warningdf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelWarning, data, format, v...)
}

// Errordf is equivalent to Infodf but uses a different logging level
func (l *JLogger) Errordf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelError, data, format, v...)
}

// Fataldf is equivalent to Infodf but uses a different logging level and calls fatal afterwards
func (l *JLogger) Fataldf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelFatal, data, format, v...)
}

// Panicdf is equivalent to Infodf but uses a different logging level and calls fatal afterwards
func (l *JLogger) Panicdf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelPanic, data, format, v...)
}

// Infof is similar to log.Printf but uses a special markup indicating the message severity
func (l *JLogger) Infof(format string, v ...interface{}) {
	l.print(LogLevelInfo, format, v...)
}

// Debugf is equivalent to Infof but uses a different logging level
func (l *JLogger) Debugf(format string, v ...interface{}) {
	l.print(LogLevelDebug, format, v...)
}

// Warningf is equivalent to Infof but uses a different logging level
func (l *JLogger) Warningf(format string, v ...interface{}) {
	l.print(LogLevelWarning, format, v...)
}

// Errorf is equivalent to Infof but uses a different logging level
func (l *JLogger) Errorf(format string, v ...interface{}) {
	l.print(LogLevelFatal, format, v...)
}

// Fatalf is equivalent to Infof but uses a different logging level and calls fatal afterwards
func (l *JLogger) Fatalf(format string, v ...interface{}) {
	l.print(LogLevelFatal, format, v...)
}

// Panicf is equivalent to Infof but uses a different logging level and calls panic afterwards
func (l *JLogger) Panicf(format string, v ...interface{}) {
	l.print(LogLevelPanic, format, v...)
}

// Infod is equivalent to Info but takes additional data to display
func (l *JLogger) Infod(data JLData, v ...interface{}) {
	l.printData(LogLevelInfo, data, "", v...)
}

// Debugd is equivalent to Infod but uses a different logging level
func (l *JLogger) Debugd(data JLData, v ...interface{}) {
	l.printData(LogLevelDebug, data, "", v...)
}

// Warningd is equivalent to Infod but uses a different logging level
func (l *JLogger) Warningd(data JLData, v ...interface{}) {
	l.printData(LogLevelWarning, data, "", v...)
}

// Errord is equivalent to Infod but uses a different logging level
func (l *JLogger) Errord(data JLData, v ...interface{}) {
	l.printData(LogLevelError, data, "", v...)
}

// Fatald is equivalent to Infod but uses a different logging level and calls fatal afterwards
func (l *JLogger) Fatald(data JLData, v ...interface{}) {
	l.printData(LogLevelFatal, data, "", v...)
}

// Panicd is equivalent to Infod but uses a different logging level and calls panic afterwards
func (l *JLogger) Panicd(data JLData, v ...interface{}) {
	l.printData(LogLevelPanic, data, "", v...)
}

// Info is similar to log.Print but uses a special markup indicating the message severity
func (l *JLogger) Info(v ...interface{}) {
	l.print(LogLevelInfo, "", v...)
}

// Debug is equivalent to Info but uses a different logging level
func (l *JLogger) Debug(v ...interface{}) {
	l.print(LogLevelDebug, "", v...)
}

// Warning is equivalent to Info but uses a different logging level
func (l *JLogger) Warning(v ...interface{}) {
	l.print(LogLevelWarning, "", v...)
}

// Error is equivalent to Info but uses a different logging level
func (l *JLogger) Error(v ...interface{}) {
	l.print(LogLevelError, "", v...)
}

// Fatal is equivalent to Info but uses a different logging level and calls fatal afterwards
func (l *JLogger) Fatal(v ...interface{}) {
	l.print(LogLevelFatal, "", v...)
}

// Panic is equivalent to Info but uses a different logging level and calls panic afterwards
func (l *JLogger) Panic(v ...interface{}) {
	l.print(LogLevelPanic, "", v...)
}

func (l *JLogger) printData(level uint, data JLData, format string, v ...interface{}) {
	if level >= l.minLevel {
		var msg string

		if format != "" {
			msg = fmt.Sprintf(format, v...)
		} else {
			msg = fmt.Sprint(v...)
		}

		if l.ansiMode {
			color := levelToColor(level)
			out := fmt.Sprintf("\x1b[%dm[%s]\x1b[0m %s\n     \x1b[%[1]dm→ ", color, levelToString(level), msg)

			for key, val := range data {
				out += fmt.Sprintf("\x1b[%dm%v\x1b[0m=%v ", color, key, val)
			}

			l.log.Output(2, out)
		} else {
			out := fmt.Sprintf("[%s] %s\n     → ", levelToString(level), msg)

			for key, val := range data {
				out += fmt.Sprintf("%v=%v ", key, val)
			}

			l.log.Output(2, out)
		}

		if level == LogLevelFatal {
			os.Exit(1)
		} else if level == LogLevelPanic {
			panic(msg)
		}
	}
}

func (l *JLogger) print(level uint, format string, v ...interface{}) {
	if level >= l.minLevel {
		var msg string

		if format != "" {
			msg = fmt.Sprintf(format, v...)
		} else {
			msg = fmt.Sprint(v...)
		}

		if l.ansiMode {
			l.log.Output(2, fmt.Sprintf("\x1b[%dm[%s]\x1b[0m %s", levelToColor(level), levelToString(level), msg))
		} else {
			l.log.Output(2, fmt.Sprintf("[%s] %s", levelToString(level), msg))
		}

		if level == LogLevelFatal {
			os.Exit(1)
		} else if level == LogLevelPanic {
			panic(msg)
		}
	}
}
<commit_msg>fixed debug level in logger<commit_after>package jantar

import (
	"fmt"
	"io"
	"log"
	"os"
)

// Defines the various logging level
const (
	LogLevelDebug   = iota
	LogLevelInfo    = iota
	LogLevelWarning = iota
	LogLevelError   = iota
	LogLevelFatal   = iota
	LogLevelPanic   = iota
)

const (
	nocolor = 0
	red     = 31
	green   = 32
	yellow  = 33
	blue    = 34
)

// JLogger is a simple Log with some handy functions. Largely inspired by https://github.com/Sirupsen/logrus
type JLogger struct {
	log      *log.Logger
	minLevel uint
	ansiMode bool
}

// JLData is a type describing map that can be passed to Data<Level>f functions for debug output
type JLData map[string]interface{}

func levelToColor(level uint) int {
	switch level {
	case LogLevelDebug:
		return green
	case LogLevelInfo:
		return blue
	case LogLevelWarning:
		return yellow
	case LogLevelError:
		fallthrough
	case LogLevelFatal:
		fallthrough
	case LogLevelPanic:
		return red
	default:
		return nocolor
	}
}

func levelToString(level uint) string {
	switch level {
	case LogLevelDebug:
		return "DEBU"
	case LogLevelInfo:
		return "INFO"
	case LogLevelWarning:
		return "WARN"
	case LogLevelError:
		return "ERRO"
	case LogLevelFatal:
		return "FATA"
	case LogLevelPanic:
		return "PANI"
	default:
		return "Unknown"
	}
}

// NewJLogger creates a new JLogger instance
func NewJLogger(out io.Writer, prefix string, minLevel uint) *JLogger {
	ansiMode := false
	if isTerminal(os.Stderr.Fd()) {
		ansiMode = true
	}

	return &JLogger{log.New(out, prefix, 0), minLevel, ansiMode}
}

// SetMinLevel changes the loggers current minimal logging level
func (l *JLogger) SetMinLevel(minLevel uint) {
	if minLevel < LogLevelDebug || minLevel > LogLevelPanic {
		l.minLevel = LogLevelInfo
	} else {
		l.minLevel = minLevel
	}
}

// Infodf is equivalent to Infof but takes additional data to display
func (l *JLogger) Infodf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelInfo, data, format, v...)
}

// Debugdf is equivalent to Infodf but uses a different logging level
func (l *JLogger) Debugdf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelDebug, data, format, v...)
}

// Warningdf is equivalent to Infodf but uses a different logging level
func (l *JLogger) Warningdf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelWarning, data, format, v...)
}

// Errordf is equivalent to Infodf but uses a different logging level
func (l *JLogger) Errordf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelError, data, format, v...)
}

// Fataldf is equivalent to Infodf but uses a different logging level and calls fatal afterwards
func (l *JLogger) Fataldf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelFatal, data, format, v...)
}

// Panicdf is equivalent to Infodf but uses a different logging level and calls fatal afterwards
func (l *JLogger) Panicdf(data JLData, format string, v ...interface{}) {
	l.printData(LogLevelPanic, data, format, v...)
}

// Infof is similar to log.Printf but uses a special markup indicating the message severity
func (l *JLogger) Infof(format string, v ...interface{}) {
	l.print(LogLevelInfo, format, v...)
}

// Debugf is equivalent to Infof but uses a different logging level
func (l *JLogger) Debugf(format string, v ...interface{}) {
	l.print(LogLevelDebug, format, v...)
}

// Warningf is equivalent to Infof but uses a different logging level
func (l *JLogger) Warningf(format string, v ...interface{}) {
	l.print(LogLevelWarning, format, v...)
}

// Errorf is equivalent to Infof but uses a different logging level
func (l *JLogger) Errorf(format string, v ...interface{}) {
	l.print(LogLevelFatal, format, v...)
}

// Fatalf is equivalent to Infof but uses a different logging level and calls fatal afterwards
func (l *JLogger) Fatalf(format string, v ...interface{}) {
	l.print(LogLevelFatal, format, v...)
}

// Panicf is equivalent to Infof but uses a different logging level and calls panic afterwards
func (l *JLogger) Panicf(format string, v ...interface{}) {
	l.print(LogLevelPanic, format, v...)
}

// Infod is equivalent to Info but takes additional data to display
func (l *JLogger) Infod(data JLData, v ...interface{}) {
	l.printData(LogLevelInfo, data, "", v...)
}

// Debugd is equivalent to Infod but uses a different logging level
func (l *JLogger) Debugd(data JLData, v ...interface{}) {
	l.printData(LogLevelDebug, data, "", v...)
}

// Warningd is equivalent to Infod but uses a different logging level
func (l *JLogger) Warningd(data JLData, v ...interface{}) {
	l.printData(LogLevelWarning, data, "", v...)
}

// Errord is equivalent to Infod but uses a different logging level
func (l *JLogger) Errord(data JLData, v ...interface{}) {
	l.printData(LogLevelError, data, "", v...)
}

// Fatald is equivalent to Infod but uses a different logging level and calls fatal afterwards
func (l *JLogger) Fatald(data JLData, v ...interface{}) {
	l.printData(LogLevelFatal, data, "", v...)
}

// Panicd is equivalent to Infod but uses a different logging level and calls panic afterwards
func (l *JLogger) Panicd(data JLData, v ...interface{}) {
	l.printData(LogLevelPanic, data, "", v...)
}

// Info is similar to log.Print but uses a special markup indicating the message severity
func (l *JLogger) Info(v ...interface{}) {
	l.print(LogLevelInfo, "", v...)
}

// Debug is equivalent to Info but uses a different logging level
func (l *JLogger) Debug(v ...interface{}) {
	l.print(LogLevelDebug, "", v...)
}

// Warning is equivalent to Info but uses a different logging level
func (l *JLogger) Warning(v ...interface{}) {
	l.print(LogLevelWarning, "", v...)
}

// Error is equivalent to Info but uses a different logging level
func (l *JLogger) Error(v ...interface{}) {
	l.print(LogLevelError, "", v...)
}

// Fatal is equivalent to Info but uses a different logging level and calls fatal afterwards
func (l *JLogger) Fatal(v ...interface{}) {
	l.print(LogLevelFatal, "", v...)
}

// Panic is equivalent to Info but uses a different logging level and calls panic afterwards
func (l *JLogger) Panic(v ...interface{}) {
	l.print(LogLevelPanic, "", v...)
}

func (l *JLogger) printData(level uint, data JLData, format string, v ...interface{}) {
	if level >= l.minLevel {
		var msg string

		if format != "" {
			msg = fmt.Sprintf(format, v...)
		} else {
			msg = fmt.Sprint(v...)
		}

		if l.ansiMode {
			color := levelToColor(level)
			out := fmt.Sprintf("\x1b[%dm[%s]\x1b[0m %s\n     \x1b[%[1]dm→ ", color, levelToString(level), msg)

			for key, val := range data {
				out += fmt.Sprintf("\x1b[%dm%v\x1b[0m=%v ", color, key, val)
			}

			l.log.Output(2, out)
		} else {
			out := fmt.Sprintf("[%s] %s\n     → ", levelToString(level), msg)

			for key, val := range data {
				out += fmt.Sprintf("%v=%v ", key, val)
			}

			l.log.Output(2, out)
		}

		if level == LogLevelFatal {
			os.Exit(1)
		} else if level == LogLevelPanic {
			panic(msg)
		}
	}
}

func (l *JLogger) print(level uint, format string, v ...interface{}) {
	if level >= l.minLevel {
		var msg string

		if format != "" {
			msg = fmt.Sprintf(format, v...)
		} else {
			msg = fmt.Sprint(v...)
		}

		if l.ansiMode {
			l.log.Output(2, fmt.Sprintf("\x1b[%dm[%s]\x1b[0m %s", levelToColor(level), levelToString(level), msg))
		} else {
			l.log.Output(2, fmt.Sprintf("[%s] %s", levelToString(level), msg))
		}

		if level == LogLevelFatal {
			os.Exit(1)
		} else if level == LogLevelPanic {
			panic(msg)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2013 The Camlistore Authors.

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 (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"camlistore.org/pkg/cmdmain"
)

var (
	noBuild  = flag.Bool("nobuild", false, "do not rebuild anything")
	quiet, _ = strconv.ParseBool(os.Getenv("CAMLI_QUIET"))
	// Whether to build the subcommand with sqlite support. This only
	// concerns the server subcommand, which sets it to serverCmd.sqlite.
	withSqlite bool
)

// The path to the Camlistore source tree. Any devcam command
// should be run from there.
var camliSrcRoot string

// sysExec is set to syscall.Exec on platforms that support it.
var sysExec func(argv0 string, argv []string, envv []string) (err error)

// runExec execs bin. If the platform doesn't support exec, it runs it and waits
// for it to finish.
func runExec(bin string, args []string, env *Env) error {
	if sysExec != nil {
		sysExec(bin, append([]string{filepath.Base(bin)}, args...), env.Flat())
	}

	cmd := exec.Command(bin, args...)
	cmd.Env = env.Flat()
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("Could not run %v: %v", bin, err)
	}
	go handleSignals(cmd.Process)
	return cmd.Wait()
}

// cpDir copies the contents of src dir into dst dir.
// filter is a list of file suffixes to skip. ex: ".go"
func cpDir(src, dst string, filter []string) error {
	return filepath.Walk(src, func(fullpath string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		for _, suffix := range filter {
			if strings.HasSuffix(fi.Name(), suffix) {
				return nil
			}
		}
		suffix, err := filepath.Rel(src, fullpath)
		if err != nil {
			return fmt.Errorf("Failed to find Rel(%q, %q): %v", src, fullpath, err)
		}
		if fi.IsDir() {
			return nil
		}
		return cpFile(fullpath, filepath.Join(dst, suffix))
	})
}

func cpFile(src, dst string) error {
	sfi, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !sfi.Mode().IsRegular() {
		return fmt.Errorf("cpFile can't deal with non-regular file %s", src)
	}

	dstDir := filepath.Dir(dst)
	if err := os.MkdirAll(dstDir, 0755); err != nil {
		return err
	}

	df, err := os.Create(dst)
	if err != nil {
		return err
	}
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sf.Close()

	n, err := io.Copy(df, sf)
	if err == nil && n != sfi.Size() {
		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
	}
	cerr := df.Close()
	if err == nil {
		err = cerr
	}
	return err
}

func handleSignals(camliProc *os.Process) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
	for {
		sig := <-c
		sysSig, ok := sig.(syscall.Signal)
		if !ok {
			log.Fatal("Not a unix signal")
		}
		switch sysSig {
		case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT:
			log.Printf("Received %v signal, terminating.", sig)
			err := camliProc.Kill()
			if err != nil {
				log.Fatalf("Failed to kill child: %v ", err)
			}
		default:
			log.Fatal("Received another signal, should not happen.")
		}
	}
}

func checkCamliSrcRoot() {
	if _, err := os.Stat("make.go"); err != nil {
		if !os.IsNotExist(err) {
			log.Fatalf("Could not stat make.go: %v", err)
		}
		log.Fatal("./make.go not found; devcam needs to be run from the Camlistore source tree root.")
	}
	cwd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	camliSrcRoot = cwd
}

// Build builds the camlistore command at the given path from the source tree root.
func build(path string) error {
	_, cmdName := filepath.Split(path)
	target := filepath.Join("camlistore.org", path)
	binPath := filepath.Join("bin", cmdName)
	var modtime int64
	fi, err := os.Stat(binPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("Could not stat %v: %v", binPath, err)
		}
	} else {
		modtime = fi.ModTime().Unix()
	}
	args := []string{
		"run", "make.go",
		"--quiet",
		"--embed_static=false",
		"--sqlite=" + strconv.FormatBool(withSqlite),
		fmt.Sprintf("--if_mods_since=%d", modtime),
		"--targets=" + target,
	}
	cmd := exec.Command("go", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("Error building %v: %v", target, err)
	}
	return nil
}

func main() {
	checkCamliSrcRoot()
	// TODO(mpl): usage error is not really correct for devcam.
	// See if I can reimplement it while still using cmdmain.Main().
	cmdmain.Main()
}
<commit_msg>devcam: add -race flag<commit_after>/*
Copyright 2013 The Camlistore Authors.

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 (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"

	"camlistore.org/pkg/cmdmain"
)

var (
	noBuild  = flag.Bool("nobuild", false, "do not rebuild anything")
	race     = flag.Bool("race", false, "build with race detector")
	quiet, _ = strconv.ParseBool(os.Getenv("CAMLI_QUIET"))
	// Whether to build the subcommand with sqlite support. This only
	// concerns the server subcommand, which sets it to serverCmd.sqlite.
	withSqlite bool
)

// The path to the Camlistore source tree. Any devcam command
// should be run from there.
var camliSrcRoot string

// sysExec is set to syscall.Exec on platforms that support it.
var sysExec func(argv0 string, argv []string, envv []string) (err error)

// runExec execs bin. If the platform doesn't support exec, it runs it and waits
// for it to finish.
func runExec(bin string, args []string, env *Env) error {
	if sysExec != nil {
		sysExec(bin, append([]string{filepath.Base(bin)}, args...), env.Flat())
	}

	cmd := exec.Command(bin, args...)
	cmd.Env = env.Flat()
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("Could not run %v: %v", bin, err)
	}
	go handleSignals(cmd.Process)
	return cmd.Wait()
}

// cpDir copies the contents of src dir into dst dir.
// filter is a list of file suffixes to skip. ex: ".go"
func cpDir(src, dst string, filter []string) error {
	return filepath.Walk(src, func(fullpath string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		for _, suffix := range filter {
			if strings.HasSuffix(fi.Name(), suffix) {
				return nil
			}
		}
		suffix, err := filepath.Rel(src, fullpath)
		if err != nil {
			return fmt.Errorf("Failed to find Rel(%q, %q): %v", src, fullpath, err)
		}
		if fi.IsDir() {
			return nil
		}
		return cpFile(fullpath, filepath.Join(dst, suffix))
	})
}

func cpFile(src, dst string) error {
	sfi, err := os.Stat(src)
	if err != nil {
		return err
	}
	if !sfi.Mode().IsRegular() {
		return fmt.Errorf("cpFile can't deal with non-regular file %s", src)
	}

	dstDir := filepath.Dir(dst)
	if err := os.MkdirAll(dstDir, 0755); err != nil {
		return err
	}

	df, err := os.Create(dst)
	if err != nil {
		return err
	}
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sf.Close()

	n, err := io.Copy(df, sf)
	if err == nil && n != sfi.Size() {
		err = fmt.Errorf("copied wrong size for %s -> %s: copied %d; want %d", src, dst, n, sfi.Size())
	}
	cerr := df.Close()
	if err == nil {
		err = cerr
	}
	return err
}

func handleSignals(camliProc *os.Process) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
	for {
		sig := <-c
		sysSig, ok := sig.(syscall.Signal)
		if !ok {
			log.Fatal("Not a unix signal")
		}
		switch sysSig {
		case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT:
			log.Printf("Received %v signal, terminating.", sig)
			err := camliProc.Kill()
			if err != nil {
				log.Fatalf("Failed to kill child: %v ", err)
			}
		default:
			log.Fatal("Received another signal, should not happen.")
		}
	}
}

func checkCamliSrcRoot() {
	if _, err := os.Stat("make.go"); err != nil {
		if !os.IsNotExist(err) {
			log.Fatalf("Could not stat make.go: %v", err)
		}
		log.Fatal("./make.go not found; devcam needs to be run from the Camlistore source tree root.")
	}
	cwd, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}
	camliSrcRoot = cwd
}

// Build builds the camlistore command at the given path from the source tree root.
func build(path string) error {
	_, cmdName := filepath.Split(path)
	target := filepath.Join("camlistore.org", path)
	binPath := filepath.Join("bin", cmdName)
	var modtime int64
	fi, err := os.Stat(binPath)
	if err != nil {
		if !os.IsNotExist(err) {
			return fmt.Errorf("Could not stat %v: %v", binPath, err)
		}
	} else {
		modtime = fi.ModTime().Unix()
	}
	args := []string{
		"run", "make.go",
		"--quiet",
		"--race=" + strconv.FormatBool(*race),
		"--embed_static=false",
		"--sqlite=" + strconv.FormatBool(withSqlite),
		fmt.Sprintf("--if_mods_since=%d", modtime),
		"--targets=" + target,
	}
	cmd := exec.Command("go", args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("Error building %v: %v", target, err)
	}
	return nil
}

func main() {
	checkCamliSrcRoot()
	// TODO(mpl): usage error is not really correct for devcam.
	// See if I can reimplement it while still using cmdmain.Main().
	cmdmain.Main()
}
<|endoftext|>