<commit_before>package sensu

import "fmt"

type check struct {
	Name        string
	Command     string
	Subscribers []interface{}
	Interval    int
}

// GetChecks Return the list of checks
func (s *Sensu) GetChecks() ([]interface{}, error) {
	return s.GetList("checks", 0, 0)
}

// GetCheck Return check info for a specific check
func (s *Sensu) GetCheck(check string) (map[string]interface{}, error) {
	return s.Get(fmt.Sprintf("checks/%s", check))
}

// RequestCheck Issues a check request
func (s *Sensu) RequestCheck(checkName string) (map[string]interface{}, error) {
	return s.GetCheck(checkName)
	/*	rawcheck, err := s.GetCheck(checkName)
		if err != nil {
			return nil, fmt.Errorf("Can't RequestCheck for %s, error retrieving check: %s", checkName, err)
		}
		check := rawcheck.
		payload := fmt.Printf("{ \"check\": \"%s\", \"subscriber\": %v}", check["name"], json.Marshall(check["subscribers"]))
		return s.PostPayload(fmt.Sprintf("check/request"))
	*/
}
<commit_msg>Remove unused struct<commit_after>package sensu

import "fmt"

// GetChecks Return the list of checks
func (s *Sensu) GetChecks() ([]interface{}, error) {
	return s.GetList("checks", 0, 0)
}

// GetCheck Return check info for a specific check
func (s *Sensu) GetCheck(check string) (map[string]interface{}, error) {
	return s.Get(fmt.Sprintf("checks/%s", check))
}

// RequestCheck Issues a check request
func (s *Sensu) RequestCheck(checkName string) (map[string]interface{}, error) {
	return s.GetCheck(checkName)
	/*	rawcheck, err := s.GetCheck(checkName)
		if err != nil {
			return nil, fmt.Errorf("Can't RequestCheck for %s, error retrieving check: %s", checkName, err)
		}
		check := rawcheck.
		payload := fmt.Printf("{ \"check\": \"%s\", \"subscriber\": %v}", check["name"], json.Marshall(check["subscribers"]))
		return s.PostPayload(fmt.Sprintf("check/request"))
	*/
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"runtime"
	"strings"
	"time"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
)

type host struct {
	name     string
	protocol string
	endpoint string
	addrs    []string
	resps    []resp
}

type resp struct {
	id   int
	seq  int
	code int
	sent time.Time // TODO: Store as time.
	recv time.Time // TODO: Store as time.
	dur  time.Duration
	to   string
	from string
	body string // TODO: Find a more optimal storage. Works well for ICMP, but not for GET.
}

var hosts []host

// readHosts reads in hosts from a file and populates hosts []host.
func readHosts() {
	f, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		hosts = append(hosts, host{name: scanner.Text()})
		// TODO: parse for urls, set host.protocol and host.endpoint. net/url.Parse seems like a good fit.
	}
	if err := scanner.Err(); err != nil {
		log.Printf("error reading hosts from %s:%s\n", filename, err)
	}
}

var filename string

func init() {
	// Check for '-f' flag for host file
	flag.StringVar(&filename, "f", "hosts", "File with hosts and urls to check.")
	flag.Parse()
}

func (h *host) htoi() error {
	if len(h.addrs) == 0 {
		addrs, err := net.LookupHost(h.name)
		for _, i := range addrs {
			// Skip if address is ipv6.
			if strings.Contains(i, ":") {
				continue
			}
			h.addrs = append(h.addrs, i)
		}
		return err
	}
	return nil
}

func (h *host) ping() {
	switch runtime.GOOS {
	case "darwin":
	case "linux":
		log.Println("you may need to adjust the net.ipv4.ping_group_range kernel state")
	default:
		log.Println("not supported on", runtime.GOOS)
		return
	}

	c, err := icmp.ListenPacket("udp4", "0.0.0.0")
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	s := fmt.Sprintf("Ranger-Chooch-%s", h.name)
	var to net.IP
	var from string
	var sent, recv time.Time

	// TODO: icmp echo forever
	for i := 0; i < 1; i++ {
		wm := icmp.Message{
			Type: ipv4.ICMPTypeEcho, Code: 0,
			/* TODO: Use ID and Data as a sanity check for Echo Replies.
			 * Using ID was from the example from golang.org, and /sbin/ping uses its PID as ICMP's ID.
			 * Maybe there's a reason for this, and we should keep ID as our PID.
			 */

			Body: &icmp.Echo{
				ID: os.Getpid() & 0xffff, Seq: i & 0xffff,
				Data: []byte(s),
			},
		}
		wb, err := wm.Marshal(nil)
		if err != nil {
			log.Fatal(err)
		}

		// Cowardly taking the first address.
		to = net.ParseIP(h.addrs[0])

		if _, err := c.WriteTo(wb, &net.UDPAddr{IP: to}); err != nil {
			log.Fatal(err)
		}
		sent = time.Now()
	}

	rb := make([]byte, 1500)
	// TODO: icmp echoreply forever
	for i := 0; i < 1; i++ {
		n, peer, err := c.ReadFrom(rb)
		if err != nil {
			log.Fatal(err)
		}
		recv = time.Now()
		rm, err := icmp.ParseMessage(1, rb[:n])
		if err != nil {
			log.Fatal(err)
		}

		// TODO: check that ip and h.addrs[0] are the same.
		from, _, _ = net.SplitHostPort(peer.String())
		if h.addrs[0] != from {
			log.Printf("got echo reply from %s; want %s", from, to)
		}

		if rm.Type != ipv4.ICMPTypeEchoReply {
			log.Printf("received something other than ping: %d", rm.Type)
			continue
		}
		body := string(rm.Body.(*icmp.Echo).Data)
		seq := rm.Body.(*icmp.Echo).Seq
		id := rm.Body.(*icmp.Echo).ID
		dur := sent.Sub(recv)
		h.addResp(id, seq, rm.Code, sent, recv, dur, to.String(), from, body)
		time.Sleep(time.Second)

	}
}

func (h *host) addResp(id, seq, code int, sent, recv time.Time, dur time.Duration, to, from, body string) {
	r := resp{
		id:   id,
		seq:  seq,
		code: code,
		sent: sent,
		recv: recv,
		dur:  dur,
		to:   to,
		from: from,
		body: body,
	}
	h.resps = append(h.resps, r)
}

func main() {
	// if an entry is a url, send a GET request
	// if an entry is a hostname, send an ICMP ping
	// TODO: host method for GET
	// TODO: host method for ICMP
	// TODO: figure out how to represent responses.
	// TODO: store responses in google sheets.
	// TODO: cache writes to google sheets if network is unavailable.
	// TODO: rewrite host request methods as goroutines.
	// TODO: intercept control-c, stop pings, drain responses, exit.
}
<commit_msg>Remove done TODOs, Add check ICMP Data field<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"runtime"
	"strings"
	"time"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
)

type host struct {
	name     string
	protocol string
	endpoint string
	addrs    []string
	resps    []resp
}

type resp struct {
	id   int
	seq  int
	code int
	sent time.Time
	recv time.Time
	dur  time.Duration
	to   string
	from string
	body string // TODO: Find a more optimal storage. Works well for ICMP, but not for GET.
}

var hosts []host

// readHosts reads in hosts from a file and populates hosts []host.
func readHosts() {
	f, err := os.Open(filename)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		hosts = append(hosts, host{name: scanner.Text()})
	}
	if err := scanner.Err(); err != nil {
		log.Printf("error reading hosts from %s:%s\n", filename, err)
	}
}

var filename string

func init() {
	// Check for '-f' flag for host file
	flag.StringVar(&filename, "f", "hosts", "File with hosts and urls to check.")
	flag.Parse()
}

func (h *host) htoi() error {
	if len(h.addrs) == 0 {
		addrs, err := net.LookupHost(h.name)
		for _, i := range addrs {
			// Skip if address is ipv6.
			if strings.Contains(i, ":") {
				continue
			}
			h.addrs = append(h.addrs, i)
		}
		return err
	}
	return nil
}

func (h *host) ping() {
	switch runtime.GOOS {
	case "darwin":
	case "linux":
		log.Println("you may need to adjust the net.ipv4.ping_group_range kernel state")
	default:
		log.Println("not supported on", runtime.GOOS)
		return
	}

	c, err := icmp.ListenPacket("udp4", "0.0.0.0")
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	s := fmt.Sprintf("Ranger-Chooch-%s", h.name)
	var to net.IP
	var from string
	var sent, recv time.Time

	// TODO: icmp echo forever
	for i := 0; i < 1; i++ {
		wm := icmp.Message{
			Type: ipv4.ICMPTypeEcho, Code: 0,
			/* Using ID was from the example from golang.org, and /sbin/ping uses its PID as ICMP's ID.
			 * Maybe there's a reason for this, and we should keep ID as our PID.
			 */
			Body: &icmp.Echo{
				ID: os.Getpid() & 0xffff, Seq: i & 0xffff,
				Data: []byte(s),
			},
		}
		wb, err := wm.Marshal(nil)
		if err != nil {
			log.Fatal(err)
		}

		// Cowardly taking the first address.
		to = net.ParseIP(h.addrs[0])

		if _, err := c.WriteTo(wb, &net.UDPAddr{IP: to}); err != nil {
			log.Fatal(err)
		}
		sent = time.Now()
	}

	rb := make([]byte, 1500)
	// TODO: icmp echoreply forever
	for i := 0; i < 1; i++ {
		n, peer, err := c.ReadFrom(rb)
		if err != nil {
			log.Fatal(err)
		}
		recv = time.Now()
		rm, err := icmp.ParseMessage(1, rb[:n])
		if err != nil {
			log.Fatal(err)
		}

		from, _, _ = net.SplitHostPort(peer.String())
		if h.addrs[0] != from {
			log.Printf("got echo reply from %s; want %s", from, to)
		}
		if rm.Type != ipv4.ICMPTypeEchoReply {
			log.Printf("received something other than ping: %d", rm.Type)
			continue
		}

		body := string(rm.Body.(*icmp.Echo).Data)
		if s != body {
			log.Printf("got echo reply body %s; want %s", body, s)
		}
		seq := rm.Body.(*icmp.Echo).Seq
		id := rm.Body.(*icmp.Echo).ID
		dur := sent.Sub(recv)
		h.addResp(id, seq, rm.Code, sent, recv, dur, to.String(), from, body)
		time.Sleep(time.Second)

	}
}

func (h *host) addResp(id, seq, code int, sent, recv time.Time, dur time.Duration, to, from, body string) {
	r := resp{
		id:   id,
		seq:  seq,
		code: code,
		sent: sent,
		recv: recv,
		dur:  dur,
		to:   to,
		from: from,
		body: body,
	}
	h.resps = append(h.resps, r)
}

func main() {
	// if an entry is a url, send a GET request
	// if an entry is a hostname, send an ICMP ping
	// TODO: parse h.name{}'s for urls, set host.protocol and host.endpoint. net/url.Parse seems like a good fit.
	// TODO: host method for GET
	// TODO: host method for ICMP
	// TODO: store responses in google sheets.
	// TODO: cache writes to google sheets if network is unavailable.
	// TODO: rewrite host request methods as goroutines.
	// TODO: intercept control-c, stop pings, drain responses, exit.
}
<|endoftext|>


<commit_before>package services

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"log"
	"strconv"
	"time"

	"hnews/Godeps/_workspace/src/github.com/boltdb/bolt"
	"hnews/Godeps/_workspace/src/github.com/headzoo/surf"
)

var (
	newsdb, _ = bolt.Open("a.db", 0644, nil)
	commdb, _ = bolt.Open("b.db", 0644, nil)
)

/** Login service **/

// Login signs the user to Hacker News
func Login(username string, password string) bool {
	bow := surf.NewBrowser()
	err := bow.Open("https://news.ycombinator.com/login?goto=news")
	if err != nil {
		return false
	}

	fm, _ := bow.Form("form")
	fm.Input("acct", username)
	fm.Input("pw", password)
	erro := fm.Submit()
	if erro != nil {
		log.Println(erro)
		return false
	}
	log.Println(fm)

	return true
}

/** Database Service **/

// News represent one news story/item on Hacker News
type News struct {
	ID       int32     `json:"id"` // Ints need to be a defined size since binary.Read does not support just 'int'
	Rank     int32     `json:"rank"`
	Title    string    `json:"title"`
	Link     string    `json:"link"`
	Author   string    `json:"author"`
	Points   int32     `json:"points"`
	Time     time.Time `json:"time"`
	Comments int32     `json:"comments"` // Number of comments on the News
}

// ReadNews ...
func ReadNews(from int, to int) []News {
	var news []News
	newsdb.View(func(tx *bolt.Tx) error {
		for i := from; i <= to; i++ {
			b := tx.Bucket([]byte(strconv.Itoa(int(i))))
			if b == nil {
				log.Println("Bucket", i, "not found.")
				continue
			}

			title := string(b.Get([]byte("title")))
			author := string(b.Get([]byte("author")))
			link := string(b.Get([]byte("link")))

			var rank int32
			binary.Read(bytes.NewReader(b.Get([]byte("rank"))), binary.LittleEndian, &rank)

			var t int64
			binary.Read(bytes.NewReader(b.Get([]byte("time"))), binary.LittleEndian, &t)

			var points int32
			binary.Read(bytes.NewReader(b.Get([]byte("points"))), binary.LittleEndian, &points)

			var comments int32
			binary.Read(bytes.NewReader(b.Get([]byte("comments"))), binary.LittleEndian, &comments)

			var id int32
			binary.Read(bytes.NewReader(b.Get([]byte("id"))), binary.LittleEndian, &id)

			news = append(news, News{id, rank, title, link, author, points, time.Unix(t, 0), comments})
		}
		return nil
	})
	return news
}

// SaveNews saves the News in the DB
func SaveNews(news []News) {
	newsdb.Update(func(tx *bolt.Tx) error {
		for _, aNews := range news {
			b, err := tx.CreateBucketIfNotExists([]byte(strconv.Itoa(int(aNews.Rank))))
			if err != nil {
				log.Println(err)
				return err
			}

			b.Put([]byte("title"), []byte(aNews.Title))
			b.Put([]byte("link"), []byte(aNews.Link))
			b.Put([]byte("author"), []byte(aNews.Author))

			var t bytes.Buffer
			binary.Write(&t, binary.LittleEndian, aNews.Time.Unix())
			b.Put([]byte("time"), []byte(t.Bytes()))

			var r bytes.Buffer
			binary.Write(&r, binary.LittleEndian, aNews.Rank)
			b.Put([]byte("rank"), []byte(r.Bytes()))

			var p bytes.Buffer
			binary.Write(&p, binary.LittleEndian, aNews.Points)
			b.Put([]byte("points"), []byte(p.Bytes()))

			var c bytes.Buffer
			binary.Write(&c, binary.LittleEndian, aNews.Comments)
			b.Put([]byte("comments"), []byte(c.Bytes()))

			var id bytes.Buffer
			binary.Write(&id, binary.LittleEndian, aNews.ID)
			b.Put([]byte("id"), []byte(id.Bytes()))

			if err != nil {
				log.Println("Err SaveNews:", err)
				return err
			}
		}
		return nil
	})
}

// ReadNewsIds Read all the keys from News db
func ReadNewsIds() []int32 {
	var ids []int32
	newsdb.View(func(tx *bolt.Tx) error {
		for i := 1; i < 480; i++ {
			b := tx.Bucket([]byte(strconv.Itoa(int(i))))
			if b == nil {
				log.Println("Bucket", i, "not found.")
				continue
			}
			var id int32
			binary.Read(bytes.NewReader(b.Get([]byte("id"))), binary.LittleEndian, &id)
			ids = append(ids, id)
		}
		return nil

	})
	return ids
}

// A Comment on a News
type Comment struct {
	Num      int32     `json:"num"`      // The ith comment on the post
	ParentID int32     `json:"parentid"` // ID of the News
	ID       int32     `json:"id"`       // The Comments unique ID
	Offset   int32     `json:"offset"`   // Level of offset for the Comment
	Time     time.Time `json:"time"`
	Author   string    `json:"author"`
	Text     string    `json:"text"`
}

// SaveComments Dumps the Comments into the newsDB as JSON.
func SaveComments(comments []Comment) {
	if len(comments) == 0 {
		return
	}
	newsid := comments[0].ParentID
	commdb.Update(func(tx *bolt.Tx) error {
		k := strconv.Itoa(int(newsid))
		b, err := tx.CreateBucketIfNotExists([]byte(k))
		if err != nil {
			log.Println("SaveComments:", err)
			return err
		}
		for _, comment := range comments {
			v, err := json.Marshal(comment)
			if err != nil {
				log.Println("SaveComments:", err)
				continue
			}
			b.Put([]byte(strconv.Itoa(int(comment.Num))), []byte(v))
		}
		return nil
	})
}

// ReadComments Returns the comments on the News item specified by the id.
func ReadComments(newsid int, from int, to int) []Comment {
	var comments []Comment
	commdb.View(func(tx *bolt.Tx) error {
		k := strconv.Itoa(newsid)
		b := tx.Bucket([]byte(k))
		for i := from; i < to; i++ {
			k := []byte(strconv.Itoa(i))
			v := b.Get(k)
			str := string(v)
			if len(str) == 0 {
				continue
			}
			var comment Comment
			json.Unmarshal([]byte(str), &comment)
			comments = append(comments, comment)
		}
		return nil
	})
	return comments
}
<commit_msg>Fixed the comments bug.<commit_after>package services

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"log"
	"strconv"
	"time"

	"hnews/Godeps/_workspace/src/github.com/boltdb/bolt"
	"hnews/Godeps/_workspace/src/github.com/headzoo/surf"
)

var (
	newsdb, _ = bolt.Open("a.db", 0644, nil)
	commdb, _ = bolt.Open("b.db", 0644, nil)
)

/** Login service **/

// Login signs the user to Hacker News
func Login(username string, password string) bool {
	bow := surf.NewBrowser()
	err := bow.Open("https://news.ycombinator.com/login?goto=news")
	if err != nil {
		return false
	}

	fm, _ := bow.Form("form")
	fm.Input("acct", username)
	fm.Input("pw", password)
	erro := fm.Submit()
	if erro != nil {
		log.Println(erro)
		return false
	}
	log.Println(fm)

	return true
}

/** Database Service **/

// News represent one news story/item on Hacker News
type News struct {
	ID       int32     `json:"id"` // Ints need to be a defined size since binary.Read does not support just 'int'
	Rank     int32     `json:"rank"`
	Title    string    `json:"title"`
	Link     string    `json:"link"`
	Author   string    `json:"author"`
	Points   int32     `json:"points"`
	Time     time.Time `json:"time"`
	Comments int32     `json:"comments"` // Number of comments on the News
}

// ReadNews ...
func ReadNews(from int, to int) []News {
	var news []News
	newsdb.View(func(tx *bolt.Tx) error {
		for i := from; i <= to; i++ {
			b := tx.Bucket([]byte(strconv.Itoa(int(i))))
			if b == nil {
				log.Println("Bucket", i, "not found.")
				continue
			}

			title := string(b.Get([]byte("title")))
			author := string(b.Get([]byte("author")))
			link := string(b.Get([]byte("link")))

			var rank int32
			binary.Read(bytes.NewReader(b.Get([]byte("rank"))), binary.LittleEndian, &rank)

			var t int64
			binary.Read(bytes.NewReader(b.Get([]byte("time"))), binary.LittleEndian, &t)

			var points int32
			binary.Read(bytes.NewReader(b.Get([]byte("points"))), binary.LittleEndian, &points)

			var comments int32
			binary.Read(bytes.NewReader(b.Get([]byte("comments"))), binary.LittleEndian, &comments)

			var id int32
			binary.Read(bytes.NewReader(b.Get([]byte("id"))), binary.LittleEndian, &id)

			news = append(news, News{id, rank, title, link, author, points, time.Unix(t, 0), comments})
		}
		return nil
	})
	return news
}

// SaveNews saves the News in the DB
func SaveNews(news []News) {
	newsdb.Update(func(tx *bolt.Tx) error {
		for _, aNews := range news {
			b, err := tx.CreateBucketIfNotExists([]byte(strconv.Itoa(int(aNews.Rank))))
			if err != nil {
				log.Println(err)
				return err
			}

			b.Put([]byte("title"), []byte(aNews.Title))
			b.Put([]byte("link"), []byte(aNews.Link))
			b.Put([]byte("author"), []byte(aNews.Author))

			var t bytes.Buffer
			binary.Write(&t, binary.LittleEndian, aNews.Time.Unix())
			b.Put([]byte("time"), []byte(t.Bytes()))

			var r bytes.Buffer
			binary.Write(&r, binary.LittleEndian, aNews.Rank)
			b.Put([]byte("rank"), []byte(r.Bytes()))

			var p bytes.Buffer
			binary.Write(&p, binary.LittleEndian, aNews.Points)
			b.Put([]byte("points"), []byte(p.Bytes()))

			var c bytes.Buffer
			binary.Write(&c, binary.LittleEndian, aNews.Comments)
			b.Put([]byte("comments"), []byte(c.Bytes()))

			var id bytes.Buffer
			binary.Write(&id, binary.LittleEndian, aNews.ID)
			b.Put([]byte("id"), []byte(id.Bytes()))

			if err != nil {
				log.Println("Err SaveNews:", err)
				return err
			}
		}
		return nil
	})
}

// ReadNewsIds Read all the keys from News db
func ReadNewsIds() []int32 {
	var ids []int32
	newsdb.View(func(tx *bolt.Tx) error {
		for i := 1; i < 480; i++ {
			b := tx.Bucket([]byte(strconv.Itoa(int(i))))
			if b == nil {
				log.Println("Bucket", i, "not found.")
				continue
			}
			var id int32
			binary.Read(bytes.NewReader(b.Get([]byte("id"))), binary.LittleEndian, &id)
			ids = append(ids, id)
		}
		return nil

	})
	return ids
}

// A Comment on a News
type Comment struct {
	Num      int32     `json:"num"`      // The ith comment on the post
	ParentID int32     `json:"parentid"` // ID of the News
	ID       int32     `json:"id"`       // The Comments unique ID
	Offset   int32     `json:"offset"`   // Level of offset for the Comment
	Time     time.Time `json:"time"`
	Author   string    `json:"author"`
	Text     string    `json:"text"`
}

// SaveComments Dumps the Comments into the newsDB as JSON.
func SaveComments(comments []Comment) {
	if len(comments) == 0 {
		return
	}
	newsid := comments[0].ParentID
	commdb.Update(func(tx *bolt.Tx) error {
		k := strconv.Itoa(int(newsid))
		b, err := tx.CreateBucketIfNotExists([]byte(k))
		if err != nil {
			log.Println("SaveComments:", err)
			return err
		}
		for _, comment := range comments {
			v, err := json.Marshal(comment)
			if err != nil {
				log.Println("SaveComments:", err)
				continue
			}
			b.Put([]byte(strconv.Itoa(int(comment.Num))), []byte(v))
		}
		return nil
	})
}

// ReadComments Returns the comments on the News item specified by the id.
func ReadComments(newsid int, from int, to int) []Comment {
	var comments []Comment
	commdb.View(func(tx *bolt.Tx) error {
		k := strconv.Itoa(newsid)
		b := tx.Bucket([]byte(k))
		if b == nil {
			return nil
		}
		for i := from; i < to; i++ {
			k := []byte(strconv.Itoa(i))
			v := b.Get(k)
			str := string(v)
			if len(str) == 0 {
				continue
			}
			var comment Comment
			json.Unmarshal([]byte(str), &comment)
			comments = append(comments, comment)
		}
		return nil
	})
	return comments
}
<|endoftext|>


<commit_before>package bucketeer

import (
	"fmt"

	"github.com/boltdb/bolt"
)

type ByteBucketeer struct {
	db   *bolt.DB
	path Path
}

func NewByteBucketeer(db *bolt.DB, path Path) (bb *ByteBucketeer) {
	bb = &ByteBucketeer{
		db:   db,
		path: path,
	}
	return
}

func (bb *ByteBucketeer) EnsurePathBuckets() (err error) {
	err = EnsurePathBuckets(bb.db, bb.path)
	return
}

func (bb *ByteBucketeer) EnsureNestedBucket(bucket string) (err error) {
	err = EnsureNestedBucket(bb.db, bb.path, bucket)
	return
}

func (bb *ByteBucketeer) Put(key []byte, value []byte) error {
	return PutByteValue(bb.db, bb.path, key, value)
}

func (bb *ByteBucketeer) Get(key []byte) ([]byte, error) {
	return GetByteValue(bb.db, bb.path, key)
}

func (bb *ByteBucketeer) Delete(key []byte) error {
	return DeleteKey(bb.db, bb.path, key)
}

func (bb *ByteBucketeer) PutNested(bucket string, key []byte, value []byte) error {
	return PutByteValue(bb.db, bb.path.Nest(bucket), key, value)
}

func (bb *ByteBucketeer) GetNested(bucket string, key []byte) ([]byte, error) {
	return GetByteValue(bb.db, bb.path.Nest(bucket), key)
}

func (bb *ByteBucketeer) DeleteNested(bucket string, key []byte) error {
	return DeleteKey(bb.db, bb.path.Nest(bucket), key)
}

func PutByteValue(db *bolt.DB, path Path, key []byte, value []byte) (err error) {
	txf := func(tx *bolt.Tx) (err error) {
		var b *bolt.Bucket
		if b = GetBucket(tx, path); b == nil {
			err = fmt.Errorf("Did not find one or more path buckets: %s", path.String())
			return
		}
		err = b.Put(key, value)
		return
	}
	err = db.Update(txf)
	return
}

/*
GetByteValue gets the key's value as a byte slice.
*/
func GetByteValue(db *bolt.DB, path Path, key []byte) (valueCopy []byte, err error) {
	txf := func(tx *bolt.Tx) error {
		if value := GetValueInTx(tx, path, key); value != nil {
			valueCopy = make([]byte, len(value))
			copy(valueCopy, value)
		}
		return nil
	}
	err = db.View(txf)
	return
}

func GetValueInTx(tx *bolt.Tx, path Path, key []byte) (value []byte) {
	if b := GetBucket(tx, path); b != nil {
		value = b.Get(key)
	}
	return
}

func DeleteKey(db *bolt.DB, path Path, key []byte) (err error) {
	txf := func(tx *bolt.Tx) (err error) {
		if b := GetBucket(tx, path); b != nil {
			err = b.Delete(key)
		}
		return
	}
	err = db.Update(txf)
	return
}
<commit_msg>- removed ByteBucketeer<commit_after>package bucketeer

import (
	"fmt"

	"github.com/boltdb/bolt"
)

func PutByteValue(db *bolt.DB, path Path, key []byte, value []byte) (err error) {
	txf := func(tx *bolt.Tx) (err error) {
		var b *bolt.Bucket
		if b = GetBucket(tx, path); b == nil {
			err = fmt.Errorf("Did not find one or more path buckets: %s", path.String())
			return
		}
		err = b.Put(key, value)
		return
	}
	err = db.Update(txf)
	return
}

/*
GetByteValue gets the key's value as a byte slice.
*/
func GetByteValue(db *bolt.DB, path Path, key []byte) (valueCopy []byte, err error) {
	txf := func(tx *bolt.Tx) error {
		if value := GetValueInTx(tx, path, key); value != nil {
			valueCopy = make([]byte, len(value))
			copy(valueCopy, value)
		}
		return nil
	}
	err = db.View(txf)
	return
}

func GetValueInTx(tx *bolt.Tx, path Path, key []byte) (value []byte) {
	if b := GetBucket(tx, path); b != nil {
		value = b.Get(key)
	}
	return
}

func DeleteKey(db *bolt.DB, path Path, key []byte) (err error) {
	txf := func(tx *bolt.Tx) (err error) {
		if b := GetBucket(tx, path); b != nil {
			err = b.Delete(key)
		}
		return
	}
	err = db.Update(txf)
	return
}
<|endoftext|>


<commit_before>package ppu

import (
	"image/color"
)

var (

	// Tile bits are counted left-to-right so bit 0 of the tile is bit 7 of the byte
	patterns = []uint8{
		0b10000000,
		0b01000000,
		0b00100000,
		0b00010000,
		0b00001000,
		0b00000100,
		0b00000010,
		0b00000001,
	}

	grey = []color.RGBA{
		{0xff, 0xff, 0xff, 0xff},
		{0xaa, 0xaa, 0xaa, 0xff},
		{0x77, 0x77, 0x77, 0xff},
		{0x33, 0x33, 0x33, 0xff},
	}

	// red = []color.RGBA{
	// 	{0xff, 0xaa, 0xaa, 0xff},
	// 	{0xdd, 0x77, 0x77, 0xff},
	// 	{0xaa, 0x33, 0x33, 0xff},
	// 	{0x55, 0x00, 0x00, 0xff},
	// }

	green = []color.RGBA{
		{0xaa, 0xff, 0xaa, 0xff},
		{0x77, 0xdd, 0x77, 0xff},
		{0x33, 0xaa, 0x33, 0xff},
		{0x00, 0x55, 0x00, 0xff},
	}

	blue = []color.RGBA{
		{0xaa, 0xaa, 0xff, 0xff},
		{0x77, 0x77, 0xdd, 0xff},
		{0x33, 0x33, 0xaa, 0xff},
		{0x00, 0x00, 0x55, 0xff},
	}
)

func (ppu *PPU) renderPixel(x, y uint8) {

	var spritePixel uint8
	var spriteBehindBackground bool
	var useSpritePalette1 bool

	// Does this pixel intersect with a sprite?
	if ppu.spritesEnabled {

		for sprite, overlaps := range ppu.spriteOverlaps {
			if !overlaps {
				continue
			}
			spriteAddr := 0xfe00 + uint16(sprite*4)
			spriteX := ppu.oam.PPURead(spriteAddr + 1)
			if x+8 >= spriteX && x < spriteX {
				spriteY := ppu.oam.PPURead(spriteAddr)
				tileNumber := ppu.oam.PPURead(spriteAddr + 2)
				attributes := ppu.oam.PPURead(spriteAddr + 3)
				tileOffsetX := (x - spriteX) % 8
				tileOffsetY := (y - spriteY) % 8
				spriteBehindBackground = attributes&0x80 > 0
				flipY := attributes&0x40 > 0
				flipX := attributes&0x20 > 0
				useSpritePalette1 = attributes&0x08 > 0
				if flipX {
					tileOffsetX = 7 - tileOffsetX
				}
				if flipY {
					tileOffsetY = 7 - tileOffsetY
				}
				spritePixel = ppu.readTilePixel(int(tileNumber), tileOffsetX, tileOffsetY)
				if spritePixel > 0 {
					break
				}
			}
		}

		// Did we intersect with a foreground sprite?
		if spritePixel > 0 && !spriteBehindBackground {
			if useSpritePalette1 {
				ppu.frame.SetRGBA(int(x), int(y), blue[ppu.obp1Colour[spritePixel]])
			} else {
				ppu.frame.SetRGBA(int(x), int(y), blue[ppu.obp0Colour[spritePixel]])
			}
			return
		}

	}

	var pixel uint8

	// Does this pixel intersect with the window?
	var intersectedWindow bool
	if ppu.windowEnabled {
		wx := ppu.ReadWX()
		wy := ppu.ReadWY()
		if wx <= 166 && wy <= 143 && x >= (wx-7) && y >= wy {
			pixel = ppu.findWindowPixel(x-(wx-7), y-wy)
			intersectedWindow = true
		}
	}

	// If we didn't find a window pixel then find where this pixel intersects the background
	if !intersectedWindow && ppu.bgEnabled {
		pixel = ppu.findBackgroundPixel(x, y)
	}

	// Does this pixel intersect with a background sprite?
	if pixel == 0 && spritePixel != 0 && spriteBehindBackground {
		if useSpritePalette1 {
			ppu.frame.SetRGBA(int(x), int(y), blue[ppu.obp1Colour[spritePixel]])
		} else {
			ppu.frame.SetRGBA(int(x), int(y), blue[ppu.obp0Colour[spritePixel]])
		}
	} else {
		if intersectedWindow {
			ppu.frame.SetRGBA(int(x), int(y), green[ppu.bgpColour[pixel]])
		} else {
			ppu.frame.SetRGBA(int(x), int(y), grey[ppu.bgpColour[pixel]])
		}
	}

}

func (ppu *PPU) findWindowPixel(x, y uint8) uint8 {

	tileX := x / 8
	tileY := y / 8
	tileOffsetX := x % 8
	tileOffsetY := y % 8

	var offsetAddr uint16
	if ppu.highWindowTileMap {
		offsetAddr = 0x9c00 - 0x8000
	} else {
		offsetAddr = 0x9800 - 0x8000
	}

	var tileNumber int
	tileAddr := 32*uint16(tileY) + uint16(tileX)
	tileByte := ppu.videoRAM[offsetAddr+tileAddr]
	if ppu.lowTileData {
		tileNumber = int(tileByte)
	} else {
		tileNumber = 256 + int(int8(tileByte))
	}

	return ppu.readTilePixel(tileNumber, tileOffsetX, tileOffsetY)

}

func (ppu *PPU) findBackgroundPixel(x, y uint8) uint8 {

	scx := ppu.ReadSCX()
	scy := ppu.ReadSCY()
	tileX := (x + scx) / 8
	tileY := (y + scy) / 8
	tileOffsetX := (x + scx) % 8
	tileOffsetY := (y + scy) % 8

	var offsetAddr uint16
	if ppu.highBgTileMap {
		offsetAddr = 0x9c00 - 0x8000
	} else {
		offsetAddr = 0x9800 - 0x8000
	}

	var tileNumber int
	tileAddr := 32*uint16(tileY) + uint16(tileX)
	tileByte := ppu.videoRAM[offsetAddr+tileAddr]
	if ppu.lowTileData {
		tileNumber = int(tileByte)
	} else {
		tileNumber = 256 + int(int8(tileByte))
	}

	return ppu.readTilePixel(tileNumber, tileOffsetX, tileOffsetY)

}

func (ppu *PPU) readTilePixel(tileNumber int, tileOffsetX, tileOffsetY uint8) uint8 {
	startAddr := tileNumber * 16
	a := ppu.videoRAM[startAddr+int(tileOffsetY*2)]
	b := ppu.videoRAM[startAddr+int(tileOffsetY*2)+1]
	aset := a&patterns[tileOffsetX] > 0
	bset := b&patterns[tileOffsetX] > 0
	switch {
	case !aset && !bset:
		return 0
	case !aset && bset:
		return 1
	case aset && !bset:
		return 2
	case aset && bset:
		return 3
	default:
		panic("error reading tile")
	}
}
<commit_msg>Don't show PPU debug colours all the time ...<commit_after>package ppu

import (
	"image/color"
)

var (

	// Tile bits are counted left-to-right so bit 0 of the tile is bit 7 of the byte
	patterns = []uint8{
		0b10000000,
		0b01000000,
		0b00100000,
		0b00010000,
		0b00001000,
		0b00000100,
		0b00000010,
		0b00000001,
	}

	grey = []color.RGBA{
		{0xff, 0xff, 0xff, 0xff},
		{0xaa, 0xaa, 0xaa, 0xff},
		{0x77, 0x77, 0x77, 0xff},
		{0x33, 0x33, 0x33, 0xff},
	}

	// red = []color.RGBA{
	// 	{0xff, 0xaa, 0xaa, 0xff},
	// 	{0xdd, 0x77, 0x77, 0xff},
	// 	{0xaa, 0x33, 0x33, 0xff},
	// 	{0x55, 0x00, 0x00, 0xff},
	// }

	green = []color.RGBA{
		{0xaa, 0xff, 0xaa, 0xff},
		{0x77, 0xdd, 0x77, 0xff},
		{0x33, 0xaa, 0x33, 0xff},
		{0x00, 0x55, 0x00, 0xff},
	}

	blue = []color.RGBA{
		{0xaa, 0xaa, 0xff, 0xff},
		{0x77, 0x77, 0xdd, 0xff},
		{0x33, 0x33, 0xaa, 0xff},
		{0x00, 0x00, 0x55, 0xff},
	}
)

func (ppu *PPU) renderPixel(x, y uint8) {

	colours := grey

	var spritePixel uint8
	var spriteBehindBackground bool
	var useSpritePalette1 bool

	// Does this pixel intersect with a sprite?
	if ppu.spritesEnabled {

		for sprite, overlaps := range ppu.spriteOverlaps {
			if !overlaps {
				continue
			}
			spriteAddr := 0xfe00 + uint16(sprite*4)
			spriteX := ppu.oam.PPURead(spriteAddr + 1)
			if x+8 >= spriteX && x < spriteX {
				spriteY := ppu.oam.PPURead(spriteAddr)
				tileNumber := ppu.oam.PPURead(spriteAddr + 2)
				attributes := ppu.oam.PPURead(spriteAddr + 3)
				tileOffsetX := (x - spriteX) % 8
				tileOffsetY := (y - spriteY) % 8
				spriteBehindBackground = attributes&0x80 > 0
				flipY := attributes&0x40 > 0
				flipX := attributes&0x20 > 0
				useSpritePalette1 = attributes&0x08 > 0
				if flipX {
					tileOffsetX = 7 - tileOffsetX
				}
				if flipY {
					tileOffsetY = 7 - tileOffsetY
				}
				spritePixel = ppu.readTilePixel(int(tileNumber), tileOffsetX, tileOffsetY)
				if spritePixel > 0 {
					break
				}
			}
		}

		// Did we intersect with a foreground sprite?
		if spritePixel > 0 && !spriteBehindBackground {
			if ppu.debug {
				colours = blue
			}
			if useSpritePalette1 {
				ppu.frame.SetRGBA(int(x), int(y), colours[ppu.obp1Colour[spritePixel]])
			} else {
				ppu.frame.SetRGBA(int(x), int(y), colours[ppu.obp0Colour[spritePixel]])
			}
			return
		}

	}

	var pixel uint8

	// Does this pixel intersect with the window?
	var intersectedWindow bool
	if ppu.windowEnabled {
		wx := ppu.ReadWX()
		wy := ppu.ReadWY()
		if wx <= 166 && wy <= 143 && x >= (wx-7) && y >= wy {
			pixel = ppu.findWindowPixel(x-(wx-7), y-wy)
			intersectedWindow = true
		}
	}

	// If we didn't find a window pixel then find where this pixel intersects the background
	if !intersectedWindow && ppu.bgEnabled {
		pixel = ppu.findBackgroundPixel(x, y)
	}

	// Does this pixel intersect with a background sprite?
	if pixel == 0 && spritePixel != 0 && spriteBehindBackground {
		if ppu.debug {
			colours = blue
		}
		if useSpritePalette1 {
			ppu.frame.SetRGBA(int(x), int(y), colours[ppu.obp1Colour[spritePixel]])
		} else {
			ppu.frame.SetRGBA(int(x), int(y), colours[ppu.obp0Colour[spritePixel]])
		}
	} else {
		if intersectedWindow {
			if ppu.debug {
				colours = green
			}
			ppu.frame.SetRGBA(int(x), int(y), colours[ppu.bgpColour[pixel]])
		} else {
			ppu.frame.SetRGBA(int(x), int(y), grey[ppu.bgpColour[pixel]])
		}
	}

}

func (ppu *PPU) findWindowPixel(x, y uint8) uint8 {

	tileX := x / 8
	tileY := y / 8
	tileOffsetX := x % 8
	tileOffsetY := y % 8

	var offsetAddr uint16
	if ppu.highWindowTileMap {
		offsetAddr = 0x9c00 - 0x8000
	} else {
		offsetAddr = 0x9800 - 0x8000
	}

	var tileNumber int
	tileAddr := 32*uint16(tileY) + uint16(tileX)
	tileByte := ppu.videoRAM[offsetAddr+tileAddr]
	if ppu.lowTileData {
		tileNumber = int(tileByte)
	} else {
		tileNumber = 256 + int(int8(tileByte))
	}

	return ppu.readTilePixel(tileNumber, tileOffsetX, tileOffsetY)

}

func (ppu *PPU) findBackgroundPixel(x, y uint8) uint8 {

	scx := ppu.ReadSCX()
	scy := ppu.ReadSCY()
	tileX := (x + scx) / 8
	tileY := (y + scy) / 8
	tileOffsetX := (x + scx) % 8
	tileOffsetY := (y + scy) % 8

	var offsetAddr uint16
	if ppu.highBgTileMap {
		offsetAddr = 0x9c00 - 0x8000
	} else {
		offsetAddr = 0x9800 - 0x8000
	}

	var tileNumber int
	tileAddr := 32*uint16(tileY) + uint16(tileX)
	tileByte := ppu.videoRAM[offsetAddr+tileAddr]
	if ppu.lowTileData {
		tileNumber = int(tileByte)
	} else {
		tileNumber = 256 + int(int8(tileByte))
	}

	return ppu.readTilePixel(tileNumber, tileOffsetX, tileOffsetY)

}

func (ppu *PPU) readTilePixel(tileNumber int, tileOffsetX, tileOffsetY uint8) uint8 {
	startAddr := tileNumber * 16
	a := ppu.videoRAM[startAddr+int(tileOffsetY*2)]
	b := ppu.videoRAM[startAddr+int(tileOffsetY*2)+1]
	aset := a&patterns[tileOffsetX] > 0
	bset := b&patterns[tileOffsetX] > 0
	switch {
	case !aset && !bset:
		return 0
	case !aset && bset:
		return 1
	case aset && !bset:
		return 2
	case aset && bset:
		return 3
	default:
		panic("error reading tile")
	}
}
<|endoftext|>


<commit_before>package dnsimple

import (
	"time"

	. "github.com/motain/gocheck"
)

func (s *S) Test_GetDomains(c *C) {
	testServer.Response(202, nil, domainsExample)

	domains, err := s.client.GetDomains()

	_ = testServer.WaitRequest()

	c.Assert(err, IsNil)
	c.Assert(domains, DeepEquals, []DomainResponse{
		DomainResponse{
			Domain{
				228,
				19,
				0,
				"example.it",
				"example.it",
				"domain-token",
				"hosted",
				"",
				true,
				false,
				false,
				5,
				0,
				"",
				Jan15_3,
				Jan15_3,
			},
		},
		DomainResponse{
			Domain{
				227,
				19,
				28,
				"example.com",
				"example.com",
				"domain-token",
				"registered",
				"",
				true,
				true,
				false,
				7,
				0,
				"2015-01-16",
				Jan15_1,
				Jan16,
			},
		},
	})
}

var domainsExample = `[
  {
    "domain": {
      "id": 228,
      "user_id": 19,
      "registrant_id": null,
      "name": "example.it",
      "unicode_name": "example.it",
      "token": "domain-token",
      "state": "hosted",
      "language": null,
      "lockable": true,
      "auto_renew": false,
      "whois_protected": false,
      "record_count": 5,
      "service_count": 0,
      "expires_on": null,
      "created_at": "2014-01-15T22:03:49Z",
      "updated_at": "2014-01-15T22:03:49Z"
    }
  },
  {
    "domain": {
      "id": 227,
      "user_id": 19,
      "registrant_id": 28,
      "name": "example.com",
      "unicode_name": "example.com",
      "token": "domain-token",
      "state": "registered",
      "language": null,
      "lockable": true,
      "auto_renew": true,
      "whois_protected": false,
      "record_count": 7,
      "service_count": 0,
      "expires_on": "2015-01-16",
      "created_at": "2014-01-15T22:01:55Z",
      "updated_at": "2014-01-16T22:56:22Z"
    }
  }
]`

var Jan15_3, _ = time.Parse("2006-01-02T15:04:05Z", "2014-01-15T22:03:49Z")
var Jan15_1, _ = time.Parse("2006-01-02T15:04:05Z", "2014-01-15T22:01:55Z")
var Jan16, _ = time.Parse("2006-01-02T15:04:05Z", "2014-01-16T22:56:22Z")
<commit_msg>domain: fix tests<commit_after>package dnsimple

import (
	"time"

	. "github.com/motain/gocheck"
)

func (s *S) Test_GetDomains(c *C) {
	testServer.Response(202, nil, domainsExample)

	domains, err := s.client.GetDomains()

	_ = testServer.WaitRequest()

	c.Assert(err, IsNil)
	c.Assert(domains, DeepEquals, []Domain{
		Domain{
			228,
			19,
			0,
			"example.it",
			"example.it",
			"domain-token",
			"hosted",
			"",
			true,
			false,
			false,
			5,
			0,
			"",
			Jan15_3,
			Jan15_3,
		},
		Domain{
			227,
			19,
			28,
			"example.com",
			"example.com",
			"domain-token",
			"registered",
			"",
			true,
			true,
			false,
			7,
			0,
			"2015-01-16",
			Jan15_1,
			Jan16,
		},
	})
}

var domainsExample = `[
  {
    "domain": {
      "id": 228,
      "user_id": 19,
      "registrant_id": null,
      "name": "example.it",
      "unicode_name": "example.it",
      "token": "domain-token",
      "state": "hosted",
      "language": null,
      "lockable": true,
      "auto_renew": false,
      "whois_protected": false,
      "record_count": 5,
      "service_count": 0,
      "expires_on": null,
      "created_at": "2014-01-15T22:03:49Z",
      "updated_at": "2014-01-15T22:03:49Z"
    }
  },
  {
    "domain": {
      "id": 227,
      "user_id": 19,
      "registrant_id": 28,
      "name": "example.com",
      "unicode_name": "example.com",
      "token": "domain-token",
      "state": "registered",
      "language": null,
      "lockable": true,
      "auto_renew": true,
      "whois_protected": false,
      "record_count": 7,
      "service_count": 0,
      "expires_on": "2015-01-16",
      "created_at": "2014-01-15T22:01:55Z",
      "updated_at": "2014-01-16T22:56:22Z"
    }
  }
]`

var Jan15_3, _ = time.Parse("2006-01-02T15:04:05Z", "2014-01-15T22:03:49Z")
var Jan15_1, _ = time.Parse("2006-01-02T15:04:05Z", "2014-01-15T22:01:55Z")
var Jan16, _ = time.Parse("2006-01-02T15:04:05Z", "2014-01-16T22:56:22Z")
<|endoftext|>


<commit_before>package cmap

import (
	"encoding/json"
	"hash/fnv"
	"sync"
)

var SHARD_COUNT = 32

// A "thread" safe map of type string:Anything.
// To avoid lock bottlenecks this map is dived to several (SHARD_COUNT) map shards.
type ConcurrentMap []*ConcurrentMapShared

// A "thread" safe string to anything map.
type ConcurrentMapShared struct {
	items        map[string]interface{}
	sync.RWMutex // Read Write mutex, guards access to internal map.
}

// Creates a new concurrent map.
func New() ConcurrentMap {
	m := make(ConcurrentMap, SHARD_COUNT)
	for i := 0; i < SHARD_COUNT; i++ {
		m[i] = &ConcurrentMapShared{items: make(map[string]interface{})}
	}
	return m
}

// Returns shard under given key
func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared {
	hasher := fnv.New32()
	hasher.Write([]byte(key))
	return m[uint(hasher.Sum32())%uint(SHARD_COUNT)]
}

func (m ConcurrentMap) MSet(data map[string]interface{}) {
	for key, value := range data {
		shard := m.GetShard(key)
		shard.Lock()
		shard.items[key] = value
		shard.Unlock()
	}
}

// Sets the given value under the specified key.
func (m *ConcurrentMap) Set(key string, value interface{}) {
	// Get map shard.
	shard := m.GetShard(key)
	shard.Lock()
	shard.items[key] = value
	shard.Unlock()
}

// Sets the given value under the specified key if no value was associated with it.
func (m *ConcurrentMap) SetIfAbsent(key string, value interface{}) bool {
	// Get map shard.
	shard := m.GetShard(key)
	shard.Lock()
	_, ok := shard.items[key]
	if !ok {
		shard.items[key] = value
	}
	shard.Unlock()
	return !ok
}

// Retrieves an element from map under given key.
func (m ConcurrentMap) Get(key string) (interface{}, bool) {
	// Get shard
	shard := m.GetShard(key)
	shard.RLock()
	// Get item from shard.
	val, ok := shard.items[key]
	shard.RUnlock()
	return val, ok
}

// Returns the number of elements within the map.
func (m ConcurrentMap) Count() int {
	count := 0
	for i := 0; i < SHARD_COUNT; i++ {
		shard := m[i]
		shard.RLock()
		count += len(shard.items)
		shard.RUnlock()
	}
	return count
}

// Looks up an item under specified key
func (m *ConcurrentMap) Has(key string) bool {
	// Get shard
	shard := m.GetShard(key)
	shard.RLock()
	// See if element is within shard.
	_, ok := shard.items[key]
	shard.RUnlock()
	return ok
}

// Removes an element from the map.
func (m *ConcurrentMap) Remove(key string) {
	// Try to get shard.
	shard := m.GetShard(key)
	shard.Lock()
	delete(shard.items, key)
	shard.Unlock()
}

// Checks if map is empty.
func (m *ConcurrentMap) IsEmpty() bool {
	return m.Count() == 0
}

// Used by the Iter & IterBuffered functions to wrap two variables together over a channel,
type Tuple struct {
	Key string
	Val interface{}
}

// Returns an iterator which could be used in a for range loop.
//
// Deprecated: using IterBuffered() will get a better performence
func (m ConcurrentMap) Iter() <-chan Tuple {
	ch := make(chan Tuple)
	go func() {
		wg := sync.WaitGroup{}
		wg.Add(SHARD_COUNT)
		// Foreach shard.
		for _, shard := range m {
			go func(shard *ConcurrentMapShared) {
				// Foreach key, value pair.
				shard.RLock()
				for key, val := range shard.items {
					ch <- Tuple{key, val}
				}
				shard.RUnlock()
				wg.Done()
			}(shard)
		}
		wg.Wait()
		close(ch)
	}()
	return ch
}

// Returns a buffered iterator which could be used in a for range loop.
func (m ConcurrentMap) IterBuffered() <-chan Tuple {
	ch := make(chan Tuple, m.Count())
	go func() {
		wg := sync.WaitGroup{}
		wg.Add(SHARD_COUNT)
		// Foreach shard.
		for _, shard := range m {
			go func(shard *ConcurrentMapShared) {
				// Foreach key, value pair.
				shard.RLock()
				for key, val := range shard.items {
					ch <- Tuple{key, val}
				}
				shard.RUnlock()
				wg.Done()
			}(shard)
		}
		wg.Wait()
		close(ch)
	}()
	return ch
}

// Returns all items as map[string]interface{}
func (m ConcurrentMap) Items() map[string]interface{} {
	tmp := make(map[string]interface{})

	// Insert items to temporary map.
	for item := range m.IterBuffered() {
		tmp[item.Key] = item.Val
	}

	return tmp
}

// Return all keys as []string
func (m ConcurrentMap) Keys() []string {
	count := m.Count()
	ch := make(chan string, count)
	go func() {
		// Foreach shard.
		wg := sync.WaitGroup{}
		wg.Add(SHARD_COUNT)
		for _, shard := range m {
			go func(shard *ConcurrentMapShared) {
				// Foreach key, value pair.
				shard.RLock()
				for key := range shard.items {
					ch <- key
				}
				shard.RUnlock()
				wg.Done()
			}(shard)
		}
		wg.Wait()
		close(ch)
	}()

	// Generate keys
	keys := make([]string, count)
	for i := 0; i < count; i++ {
		keys[i] = <-ch
	}
	return keys
}

//Reviles ConcurrentMap "private" variables to json marshal.
func (m ConcurrentMap) MarshalJSON() ([]byte, error) {
	// Create a temporary map, which will hold all item spread across shards.
	tmp := make(map[string]interface{})

	// Insert items to temporary map.
	for item := range m.IterBuffered() {
		tmp[item.Key] = item.Val
	}
	return json.Marshal(tmp)
}

// Concurrent map uses Interface{} as its value, therefor JSON Unmarshal
// will probably won't know which to type to unmarshal into, in such case
// we'll end up with a value of type map[string]interface{}, In most cases this isn't
// out value type, this is why we've decided to remove this functionality.

// func (m *ConcurrentMap) UnmarshalJSON(b []byte) (err error) {
// 	// Reverse process of Marshal.

// 	tmp := make(map[string]interface{})

// 	// Unmarshal into a single map.
// 	if err := json.Unmarshal(b, &tmp); err != nil {
// 		return nil
// 	}

// 	// foreach key,value pair in temporary map insert into our concurrent map.
// 	for key, val := range tmp {
// 		m.Set(key, val)
// 	}
// 	return nil
// }
<commit_msg>Reuse hasher<commit_after>package cmap

import (
	"encoding/json"
	"hash/fnv"
	"sync"
	"hash"
)

var SHARD_COUNT = 32

// A "thread" safe map of type string:Anything.
// To avoid lock bottlenecks this map is dived to several (SHARD_COUNT) map shards.
type ConcurrentMap struct {
	hashPool *sync.Pool
	shards []*ConcurrentMapShared
}

// A "thread" safe string to anything map.
type ConcurrentMapShared struct {
	items        map[string]interface{}
	sync.RWMutex // Read Write mutex, guards access to internal map.
}

// Creates a new concurrent map.
func New() *ConcurrentMap {
	m := new(ConcurrentMap)
	m.hashPool = new(sync.Pool)
	m.shards = make([]*ConcurrentMapShared, SHARD_COUNT)
	for i := 0; i < SHARD_COUNT; i++ {
		m.shards[i] = &ConcurrentMapShared{items: make(map[string]interface{})}
	}
	return m
}

// Returns shard under given key
func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared {
	hasherAsInterface := m.hashPool.Get()
	var hasher hash.Hash32
	if hasherAsInterface == nil {
		hasher = fnv.New32()
	} else {
		hasher = hasherAsInterface.(hash.Hash32)
		hasher.Reset()
	}
	hasher.Write([]byte(key))
	sum := hasher.Sum32()
	m.hashPool.Put(hasher)
	return m.shards[uint(sum)%uint(SHARD_COUNT)]
}

func (m ConcurrentMap) MSet(data map[string]interface{}) {
	for key, value := range data {
		shard := m.GetShard(key)
		shard.Lock()
		shard.items[key] = value
		shard.Unlock()
	}
}

// Sets the given value under the specified key.
func (m *ConcurrentMap) Set(key string, value interface{}) {
	// Get map shard.
	shard := m.GetShard(key)
	shard.Lock()
	shard.items[key] = value
	shard.Unlock()
}

// Sets the given value under the specified key if no value was associated with it.
func (m *ConcurrentMap) SetIfAbsent(key string, value interface{}) bool {
	// Get map shard.
	shard := m.GetShard(key)
	shard.Lock()
	_, ok := shard.items[key]
	if !ok {
		shard.items[key] = value
	}
	shard.Unlock()
	return !ok
}

// Retrieves an element from map under given key.
func (m ConcurrentMap) Get(key string) (interface{}, bool) {
	// Get shard
	shard := m.GetShard(key)
	shard.RLock()
	// Get item from shard.
	val, ok := shard.items[key]
	shard.RUnlock()
	return val, ok
}

// Returns the number of elements within the map.
func (m ConcurrentMap) Count() int {
	count := 0
	for i := 0; i < SHARD_COUNT; i++ {
		shard := m.shards[i]
		shard.RLock()
		count += len(shard.items)
		shard.RUnlock()
	}
	return count
}

// Looks up an item under specified key
func (m *ConcurrentMap) Has(key string) bool {
	// Get shard
	shard := m.GetShard(key)
	shard.RLock()
	// See if element is within shard.
	_, ok := shard.items[key]
	shard.RUnlock()
	return ok
}

// Removes an element from the map.
func (m *ConcurrentMap) Remove(key string) {
	// Try to get shard.
	shard := m.GetShard(key)
	shard.Lock()
	delete(shard.items, key)
	shard.Unlock()
}

// Checks if map is empty.
func (m *ConcurrentMap) IsEmpty() bool {
	return m.Count() == 0
}

// Used by the Iter & IterBuffered functions to wrap two variables together over a channel,
type Tuple struct {
	Key string
	Val interface{}
}

// Returns an iterator which could be used in a for range loop.
//
// Deprecated: using IterBuffered() will get a better performence
func (m ConcurrentMap) Iter() <-chan Tuple {
	ch := make(chan Tuple)
	go func() {
		wg := sync.WaitGroup{}
		wg.Add(SHARD_COUNT)
		// Foreach shard.
		for _, shard := range m.shards {
			go func(shard *ConcurrentMapShared) {
				// Foreach key, value pair.
				shard.RLock()
				for key, val := range shard.items {
					ch <- Tuple{key, val}
				}
				shard.RUnlock()
				wg.Done()
			}(shard)
		}
		wg.Wait()
		close(ch)
	}()
	return ch
}

// Returns a buffered iterator which could be used in a for range loop.
func (m ConcurrentMap) IterBuffered() <-chan Tuple {
	ch := make(chan Tuple, m.Count())
	go func() {
		wg := sync.WaitGroup{}
		wg.Add(SHARD_COUNT)
		// Foreach shard.
		for _, shard := range m.shards {
			go func(shard *ConcurrentMapShared) {
				// Foreach key, value pair.
				shard.RLock()
				for key, val := range shard.items {
					ch <- Tuple{key, val}
				}
				shard.RUnlock()
				wg.Done()
			}(shard)
		}
		wg.Wait()
		close(ch)
	}()
	return ch
}

// Returns all items as map[string]interface{}
func (m ConcurrentMap) Items() map[string]interface{} {
	tmp := make(map[string]interface{})

	// Insert items to temporary map.
	for item := range m.IterBuffered() {
		tmp[item.Key] = item.Val
	}

	return tmp
}

// Return all keys as []string
func (m ConcurrentMap) Keys() []string {
	count := m.Count()
	ch := make(chan string, count)
	go func() {
		// Foreach shard.
		wg := sync.WaitGroup{}
		wg.Add(SHARD_COUNT)
		for _, shard := range m.shards {
			go func(shard *ConcurrentMapShared) {
				// Foreach key, value pair.
				shard.RLock()
				for key := range shard.items {
					ch <- key
				}
				shard.RUnlock()
				wg.Done()
			}(shard)
		}
		wg.Wait()
		close(ch)
	}()

	// Generate keys
	keys := make([]string, count)
	for i := 0; i < count; i++ {
		keys[i] = <-ch
	}
	return keys
}

//Reviles ConcurrentMap "private" variables to json marshal.
func (m ConcurrentMap) MarshalJSON() ([]byte, error) {
	// Create a temporary map, which will hold all item spread across shards.
	tmp := make(map[string]interface{})

	// Insert items to temporary map.
	for item := range m.IterBuffered() {
		tmp[item.Key] = item.Val
	}
	return json.Marshal(tmp)
}

// Concurrent map uses Interface{} as its value, therefor JSON Unmarshal
// will probably won't know which to type to unmarshal into, in such case
// we'll end up with a value of type map[string]interface{}, In most cases this isn't
// out value type, this is why we've decided to remove this functionality.

// func (m *ConcurrentMap) UnmarshalJSON(b []byte) (err error) {
// 	// Reverse process of Marshal.

// 	tmp := make(map[string]interface{})

// 	// Unmarshal into a single map.
// 	if err := json.Unmarshal(b, &tmp); err != nil {
// 		return nil
// 	}

// 	// foreach key,value pair in temporary map insert into our concurrent map.
// 	for key, val := range tmp {
// 		m.Set(key, val)
// 	}
// 	return nil
// }
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsfake

import (
	"errors"
	"fmt"
	"io"
	"sort"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

// Create an in-memory bucket with the given name and empty contents.
func NewFakeBucket(name string) gcs.Bucket {
	b := &bucket{name: name}
	b.mu = syncutil.NewInvariantMutex(func() { b.checkInvariants() })
	return b
}

type object struct {
	// A storage.Object representing metadata for this object. Never changes.
	metadata *storage.Object

	// The contents of the object. These never change.
	contents []byte
}

// A slice of objects compared by name.
type objectSlice []object

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

// Return the smallest i such that s[i].metadata.Name >= name, or len(s) if
// there is no such i.
func (s objectSlice) lowerBound(name string) int {
	pred := func(i int) bool {
		return s[i].metadata.Name >= name
	}

	return sort.Search(len(s), pred)
}

// Return the smallest i such that s[i].metadata.Name == name, or len(s) if
// there is no such i.
func (s objectSlice) find(name string) int {
	lb := s.lowerBound(name)
	if lb < len(s) && s[lb].metadata.Name == name {
		return lb
	}

	return len(s)
}

type bucket struct {
	name string
	mu   syncutil.InvariantMutex

	// The set of extant objects.
	//
	// INVARIANT: Strictly increasing.
	objects objectSlice // GUARDED_BY(mu)
}

// SHARED_LOCKS_REQUIRED(b.mu)
func (b *bucket) checkInvariants() {
	// Make sure 'objects' is strictly increasing.
	for i := 1; i < len(b.objects); i++ {
		objA := b.objects[i-1]
		objB := b.objects[i]
		if !(objA.metadata.Name < objB.metadata.Name) {
			panic(
				fmt.Sprintf(
					"Object names are not strictly increasing: %v vs. %v",
					objA.metadata.Name,
					objB.metadata.Name))
		}
	}
}

func (b *bucket) Name() string {
	return b.name
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) ListObjects(
	ctx context.Context,
	query *storage.Query) (listing *storage.Objects, err error) {
	b.mu.RLock()
	defer b.mu.RUnlock()

	// Set up the result object.
	listing = new(storage.Objects)

	// Handle nil queries.
	if query == nil {
		query = &storage.Query{}
	}

	// Handle defaults.
	maxResults := query.MaxResults
	if maxResults == 0 {
		maxResults = 1000
	}

	// Find where in the space of object names to start.
	nameStart := query.Prefix
	if query.Cursor != "" && query.Cursor > nameStart {
		nameStart = query.Cursor
	}

	// Find the range of indexes within the array to scan.
	indexStart := b.objects.lowerBound(nameStart)
	indexLimit := minInt(len(b.objects), indexStart+maxResults)

	// Scan the array.
	for i := indexStart; i < indexLimit; i++ {
		var o object = b.objects[i]

		// TODO(jacobsa): Handle prefixes.
		listing.Results = append(listing.Results, o.metadata)
	}

	// Set up a cursor for where to start the next scan if we didn't exhaust the
	// results.
	if indexLimit < len(b.objects) {
		listing.Next = &storage.Query{}
		*listing.Next = *query
		listing.Next.Cursor = b.objects[indexLimit].metadata.Name
	}

	return
}

func (b *bucket) NewReader(
	ctx context.Context,
	objectName string) (io.ReadCloser, error) {
	return nil, errors.New("TODO: Implement NewReader.")
}

func (b *bucket) NewWriter(
	ctx context.Context,
	attrs *storage.ObjectAttrs) (gcs.ObjectWriter, error) {
	return newObjectWriter(b, attrs), nil
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) DeleteObject(
	ctx context.Context,
	name string) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	// Do we possess the object with the given name?
	index := b.objects.find(name)
	if index == len(b.objects) {
		return errors.New("Object not found.")
	}

	// Remove the object.
	b.objects = append(b.objects[:index], b.objects[index+1:]...)

	return nil
}

// Create an object struct for the given attributes and contents.
//
// EXCLUSIVE_LOCKS_REQUIRED(b.mu)
func (b *bucket) mintObject(
	attrs *storage.ObjectAttrs,
	contents []byte) (o object) {
	// Set up metadata.
	// TODO(jacobsa): Other fields.
	o.metadata = &storage.Object{
		Bucket:   b.Name(),
		Name:     attrs.Name,
		Owner:    "user-fake",
		Size:     int64(len(contents)),
		Metadata: attrs.Metadata,
	}

	// Set up contents.
	o.contents = contents

	return
}

// Add a record for an object with the given attributes and contents, then
// return the minted metadata.
//
// LOCKS_EXCLUDED(b.mu)
func (b *bucket) addObject(
	attrs *storage.ObjectAttrs,
	contents []byte) *storage.Object {
	b.mu.Lock()
	defer b.mu.Unlock()

	// Create an object record from the given attributes.
	var o object = b.mintObject(attrs, contents)

	// Add it to our list of object.
	b.objects = append(b.objects, o)
	sort.Sort(b.objects)

	return o.metadata
}

func minInt(a, b int) int {
	if a < b {
		return a
	}

	return b
}

func maxInt(a, b int) int {
	if a > b {
		return a
	}

	return b
}
<commit_msg>Fixed a bug when overwriting an object.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsfake

import (
	"errors"
	"fmt"
	"io"
	"sort"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

// Create an in-memory bucket with the given name and empty contents.
func NewFakeBucket(name string) gcs.Bucket {
	b := &bucket{name: name}
	b.mu = syncutil.NewInvariantMutex(func() { b.checkInvariants() })
	return b
}

type object struct {
	// A storage.Object representing metadata for this object. Never changes.
	metadata *storage.Object

	// The contents of the object. These never change.
	contents []byte
}

// A slice of objects compared by name.
type objectSlice []object

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

// Return the smallest i such that s[i].metadata.Name >= name, or len(s) if
// there is no such i.
func (s objectSlice) lowerBound(name string) int {
	pred := func(i int) bool {
		return s[i].metadata.Name >= name
	}

	return sort.Search(len(s), pred)
}

// Return the smallest i such that s[i].metadata.Name == name, or len(s) if
// there is no such i.
func (s objectSlice) find(name string) int {
	lb := s.lowerBound(name)
	if lb < len(s) && s[lb].metadata.Name == name {
		return lb
	}

	return len(s)
}

type bucket struct {
	name string
	mu   syncutil.InvariantMutex

	// The set of extant objects.
	//
	// INVARIANT: Strictly increasing.
	objects objectSlice // GUARDED_BY(mu)
}

// SHARED_LOCKS_REQUIRED(b.mu)
func (b *bucket) checkInvariants() {
	// Make sure 'objects' is strictly increasing.
	for i := 1; i < len(b.objects); i++ {
		objA := b.objects[i-1]
		objB := b.objects[i]
		if !(objA.metadata.Name < objB.metadata.Name) {
			panic(
				fmt.Sprintf(
					"Object names are not strictly increasing: %v vs. %v",
					objA.metadata.Name,
					objB.metadata.Name))
		}
	}
}

func (b *bucket) Name() string {
	return b.name
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) ListObjects(
	ctx context.Context,
	query *storage.Query) (listing *storage.Objects, err error) {
	b.mu.RLock()
	defer b.mu.RUnlock()

	// Set up the result object.
	listing = new(storage.Objects)

	// Handle nil queries.
	if query == nil {
		query = &storage.Query{}
	}

	// Handle defaults.
	maxResults := query.MaxResults
	if maxResults == 0 {
		maxResults = 1000
	}

	// Find where in the space of object names to start.
	nameStart := query.Prefix
	if query.Cursor != "" && query.Cursor > nameStart {
		nameStart = query.Cursor
	}

	// Find the range of indexes within the array to scan.
	indexStart := b.objects.lowerBound(nameStart)
	indexLimit := minInt(len(b.objects), indexStart+maxResults)

	// Scan the array.
	for i := indexStart; i < indexLimit; i++ {
		var o object = b.objects[i]

		// TODO(jacobsa): Handle prefixes.
		listing.Results = append(listing.Results, o.metadata)
	}

	// Set up a cursor for where to start the next scan if we didn't exhaust the
	// results.
	if indexLimit < len(b.objects) {
		listing.Next = &storage.Query{}
		*listing.Next = *query
		listing.Next.Cursor = b.objects[indexLimit].metadata.Name
	}

	return
}

func (b *bucket) NewReader(
	ctx context.Context,
	objectName string) (io.ReadCloser, error) {
	return nil, errors.New("TODO: Implement NewReader.")
}

func (b *bucket) NewWriter(
	ctx context.Context,
	attrs *storage.ObjectAttrs) (gcs.ObjectWriter, error) {
	return newObjectWriter(b, attrs), nil
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) DeleteObject(
	ctx context.Context,
	name string) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	// Do we possess the object with the given name?
	index := b.objects.find(name)
	if index == len(b.objects) {
		return errors.New("Object not found.")
	}

	// Remove the object.
	b.objects = append(b.objects[:index], b.objects[index+1:]...)

	return nil
}

// Create an object struct for the given attributes and contents.
//
// EXCLUSIVE_LOCKS_REQUIRED(b.mu)
func (b *bucket) mintObject(
	attrs *storage.ObjectAttrs,
	contents []byte) (o object) {
	// Set up metadata.
	// TODO(jacobsa): Other fields.
	o.metadata = &storage.Object{
		Bucket:   b.Name(),
		Name:     attrs.Name,
		Owner:    "user-fake",
		Size:     int64(len(contents)),
		Metadata: attrs.Metadata,
	}

	// Set up contents.
	o.contents = contents

	return
}

// Add a record for an object with the given attributes and contents, then
// return the minted metadata.
//
// LOCKS_EXCLUDED(b.mu)
func (b *bucket) addObject(
	attrs *storage.ObjectAttrs,
	contents []byte) *storage.Object {
	b.mu.Lock()
	defer b.mu.Unlock()

	// Create an object record from the given attributes.
	var o object = b.mintObject(attrs, contents)

	// Replace an entry in or add an entry to our list of objects.
	existingIndex := b.objects.find(attrs.Name)
	if existingIndex < len(b.objects) {
		b.objects[existingIndex] = o
	} else {
		b.objects = append(b.objects, o)
		sort.Sort(b.objects)
	}

	return o.metadata
}

func minInt(a, b int) int {
	if a < b {
		return a
	}

	return b
}

func maxInt(a, b int) int {
	if a > b {
		return a
	}

	return b
}
<|endoftext|>


<commit_before>package itchio

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
)

type Client struct {
	Key           string
	HTTPClient    *http.Client
	BaseURL       string
	RetryPatterns []time.Duration
}

type Response struct {
	Errors []string
}

type User struct {
	ID       int64
	Username string
	CoverUrl string `json:"cover_url"`
}

type Game struct {
	ID  int64
	Url string
}

type Upload struct {
	ID       int64
	Filename string
	Size     int64

	OSX     bool `json:"p_osx"`
	Linux   bool `json:"p_linux"`
	Windows bool `json:"p_windows"`
	Android bool `json:"p_android"`
}

func defaultRetryPatterns() []time.Duration {
	return []time.Duration{
		1 * time.Second,
		2 * time.Second,
		4 * time.Second,
		8 * time.Second,
		16 * time.Second,
	}
}

func ClientWithKey(key string) *Client {
	return &Client{
		Key:           key,
		HTTPClient:    http.DefaultClient,
		BaseURL:       "https://itch.io/api/1",
		RetryPatterns: defaultRetryPatterns(),
	}
}

type StatusResponse struct {
	Response

	Success bool
}

func (c *Client) WharfStatus() (r StatusResponse, err error) {
	path := c.MakePath("wharf/status")

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type MyGamesResponse struct {
	Response

	Games []Game
}

func (c *Client) MyGames() (r MyGamesResponse, err error) {
	path := c.MakePath("my-games")
	log.Printf("Requesting %s\n", path)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type GameUploadsResponse struct {
	Response

	Uploads []Upload `json:"uploads"`
}

func (c *Client) GameUploads(gameID int64) (r GameUploadsResponse, err error) {
	path := c.MakePath("game/%d/uploads", gameID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type UploadDownloadResponse struct {
	Response

	Url string
}

func (c *Client) UploadDownload(uploadID int64) (r UploadDownloadResponse, err error) {
	path := c.MakePath("upload/%d/download", uploadID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return r, err
}

type NewBuildResponse struct {
	Response

	Build struct {
		ID          int64 `json:"id"`
		UploadID    int64 `json:"upload_id"`
		ParentBuild struct {
			ID int64 `json:"id"`
		} `json:"parent_build"`
	}
}

func (c *Client) CreateBuild(target string, channel string, userVersion string) (r NewBuildResponse, err error) {
	path := c.MakePath("wharf/builds")

	form := url.Values{}
	form.Add("target", target)
	form.Add("channel", channel)
	if userVersion != "" {
		form.Add("user_version", userVersion)
	}

	resp, err := c.PostForm(path, form)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type BuildFileInfo struct {
	ID      int64
	Size    int64
	State   BuildFileState
	Type    BuildFileType
	SubType BuildFileSubType

	CreatedAt string `json:"created_at"`
	UpdatedAt string `json:"updated_at"`
}

type BuildInfo struct {
	ID            int64
	ParentBuildID int64 `json:"parent_build_id"`
	State         BuildState

	Files []*BuildFileInfo

	User      User
	CreatedAt string `json:"created_at"`
	UpdatedAt string `json:"updated_at"`
}

type ChannelInfo struct {
	Name string
	Tags string

	Upload  Upload
	Head    *BuildInfo `json:"head"`
	Pending *BuildInfo `json:"pending"`
}

type ListChannelsResponse struct {
	Response

	Channels map[string]ChannelInfo
}

type GetChannelResponse struct {
	Response

	Channel ChannelInfo
}

func (c *Client) ListChannels(target string) (r ListChannelsResponse, err error) {
	form := url.Values{}
	form.Add("target", target)
	path := c.MakePath("wharf/channels?%s", form.Encode())

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

func (c *Client) GetChannel(target string, channel string) (r GetChannelResponse, err error) {
	form := url.Values{}
	form.Add("target", target)
	path := c.MakePath("wharf/channels/%s?%s", channel, form.Encode())

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type BuildFileType string

const (
	BuildFileType_PATCH     BuildFileType = "patch"
	BuildFileType_ARCHIVE                 = "archive"
	BuildFileType_SIGNATURE               = "signature"
)

type BuildFileSubType string

const (
	BuildFileSubType_DEFAULT   BuildFileSubType = "default"
	BuildFileSubType_GZIP                       = "gzip"
	BuildFileSubType_OPTIMIZED                  = "optimized"
)

type UploadType string

const (
	UploadType_MULTIPART UploadType = "multipart"
	UploadType_RESUMABLE            = "resumable"
)

type BuildState string

const (
	BuildState_STARTED    BuildState = "started"
	BuildState_PROCESSING            = "processing"
	BuildState_COMPLETED             = "completed"
	BuildState_FAILED                = "failed"
)

type BuildFileState string

const (
	BuildFileState_CREATED   BuildFileState = "created"
	BuildFileState_UPLOADING                = "uploading"
	BuildFileState_UPLOADED                 = "uploaded"
	BuildFileState_FAILED                   = "failed"
)

type ListBuildFilesResponse struct {
	Response

	Files []BuildFileInfo
}

func (c *Client) ListBuildFiles(buildID int64) (r ListBuildFilesResponse, err error) {
	path := c.MakePath("wharf/builds/%d/files", buildID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type NewBuildFileResponse struct {
	Response

	File struct {
		ID           int64
		UploadURL    string            `json:"upload_url"`
		UploadParams map[string]string `json:"upload_params"`
	}
}

func (c *Client) CreateBuildFile(buildID int64, fileType BuildFileType, fileSubType, subType BuildFileSubType, uploadType UploadType) (r NewBuildFileResponse, err error) {
	path := c.MakePath("wharf/builds/%d/files", buildID)

	form := url.Values{}
	form.Add("type", string(fileType))
	if subType != "" {
		form.Add("sub_type", string(subType))
	}
	if uploadType != "" {
		form.Add("upload_type", string(uploadType))
	}

	resp, err := c.PostForm(path, form)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type FinalizeBuildFileResponse struct {
	Response
}

func (c *Client) FinalizeBuildFile(buildID int64, fileID int64, size int64) (r FinalizeBuildFileResponse, err error) {
	path := c.MakePath("wharf/builds/%d/files/%d", buildID, fileID)

	form := url.Values{}
	form.Add("size", fmt.Sprintf("%d", size))

	resp, err := c.PostForm(path, form)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type DownloadBuildFileResponse struct {
	Response

	URL string
}

func (c *Client) DownloadBuildFile(buildID int64, fileID int64) (reader io.ReadCloser, err error) {
	path := c.MakePath("wharf/builds/%d/files/%d/download", buildID, fileID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	var r DownloadBuildFileResponse
	err = ParseAPIResponse(&r, resp)
	if err != nil {
		return
	}

	req, err := http.NewRequest("GET", r.URL, nil)
	if err != nil {
		return
	}

	// not an API request, going directly with http's DefaultClient
	dlResp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}

	if dlResp.StatusCode != 200 {
		err = fmt.Errorf("Can't download: %s", dlResp.Status)
		return
	}

	reader = dlResp.Body
	return
}

type BuildEventType string

const (
	BuildEvent_JOB_STARTED   BuildEventType = "job_started"
	BuildEvent_JOB_FAILED                   = "job_failed"
	BuildEvent_JOB_COMPLETED                = "job_completed"
)

type BuildEventData map[string]interface{}

type NewBuildEventResponse struct {
	Response
}

func (c *Client) CreateBuildEvent(buildID int64, eventType BuildEventType, message string, data BuildEventData) (r NewBuildEventResponse, err error) {
	path := c.MakePath("wharf/builds/%d/events", buildID)

	form := url.Values{}
	form.Add("type", string(eventType))
	form.Add("message", message)

	jsonData, err := json.Marshal(data)
	if err != nil {
		return
	}
	form.Add("data", string(jsonData))

	resp, err := c.PostForm(path, form)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type BuildEvent struct {
	Type    BuildEventType
	Message string
	Data    BuildEventData
}

type ListBuildEventsResponse struct {
	Response

	Events []BuildEvent
}

func (c *Client) ListBuildEvents(buildID int64) (r ListBuildEventsResponse, err error) {
	path := c.MakePath("wharf/builds/%d/events", buildID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

// Helpers

func (c *Client) Get(url string) (*http.Response, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	return c.Do(req)
}

func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) {
	req, err := http.NewRequest("POST", url, strings.NewReader(data.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	return c.Do(req)
}

func (c *Client) Do(req *http.Request) (*http.Response, error) {
	if strings.HasPrefix(c.Key, "jwt:") {
		req.Header.Add("Authorization", strings.Split(c.Key, ":")[1])
	}

	var res *http.Response
	var err error

	retryPatterns := append(c.RetryPatterns, time.Millisecond)

	for _, sleepTime := range retryPatterns {
		res, err = c.HTTPClient.Do(req)
		if res.StatusCode == 503 {
			// Rate limited, try again according to patterns.
			// following https://cloud.google.com/storage/docs/json_api/v1/how-tos/upload#exp-backoff to the letter
			res.Body.Close()
			time.Sleep(sleepTime + time.Duration(rand.Int()%1000)*time.Millisecond)
		} else {
			break
		}
	}

	return res, err
}

func (c *Client) MakePath(format string, a ...interface{}) string {
	base := strings.Trim(c.BaseURL, "/")
	subPath := strings.Trim(fmt.Sprintf(format, a...), "/")

	var key string
	if strings.HasPrefix(c.Key, "jwt:") {
		key = "jwt"
	} else {
		key = c.Key
	}
	return fmt.Sprintf("%s/%s/%s", base, key, subPath)
}

func ParseAPIResponse(dst interface{}, res *http.Response) error {
	if res == nil || res.Body == nil {
		return fmt.Errorf("No response from server")
	}

	bodyReader := res.Body
	defer bodyReader.Close()

	if res.StatusCode/100 != 2 {
		return fmt.Errorf("Server returned %s for %s", res.Status, res.Request.URL.String())
	}

	err := json.NewDecoder(bodyReader).Decode(dst)
	if err != nil {
		return err
	}

	errs := reflect.Indirect(reflect.ValueOf(dst)).FieldByName("Errors")
	if errs.Len() > 0 {
		// TODO: handle other errors too
		return errors.New(errs.Index(0).String())
	}

	return nil
}
<commit_msg>same but without the dumb<commit_after>package itchio

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
)

type Client struct {
	Key           string
	HTTPClient    *http.Client
	BaseURL       string
	RetryPatterns []time.Duration
}

type Response struct {
	Errors []string
}

type User struct {
	ID       int64
	Username string
	CoverUrl string `json:"cover_url"`
}

type Game struct {
	ID  int64
	Url string
}

type Upload struct {
	ID       int64
	Filename string
	Size     int64

	OSX     bool `json:"p_osx"`
	Linux   bool `json:"p_linux"`
	Windows bool `json:"p_windows"`
	Android bool `json:"p_android"`
}

func defaultRetryPatterns() []time.Duration {
	return []time.Duration{
		1 * time.Second,
		2 * time.Second,
		4 * time.Second,
		8 * time.Second,
		16 * time.Second,
	}
}

func ClientWithKey(key string) *Client {
	return &Client{
		Key:           key,
		HTTPClient:    http.DefaultClient,
		BaseURL:       "https://itch.io/api/1",
		RetryPatterns: defaultRetryPatterns(),
	}
}

type StatusResponse struct {
	Response

	Success bool
}

func (c *Client) WharfStatus() (r StatusResponse, err error) {
	path := c.MakePath("wharf/status")

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type MyGamesResponse struct {
	Response

	Games []Game
}

func (c *Client) MyGames() (r MyGamesResponse, err error) {
	path := c.MakePath("my-games")
	log.Printf("Requesting %s\n", path)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type GameUploadsResponse struct {
	Response

	Uploads []Upload `json:"uploads"`
}

func (c *Client) GameUploads(gameID int64) (r GameUploadsResponse, err error) {
	path := c.MakePath("game/%d/uploads", gameID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type UploadDownloadResponse struct {
	Response

	Url string
}

func (c *Client) UploadDownload(uploadID int64) (r UploadDownloadResponse, err error) {
	path := c.MakePath("upload/%d/download", uploadID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return r, err
}

type NewBuildResponse struct {
	Response

	Build struct {
		ID          int64 `json:"id"`
		UploadID    int64 `json:"upload_id"`
		ParentBuild struct {
			ID int64 `json:"id"`
		} `json:"parent_build"`
	}
}

func (c *Client) CreateBuild(target string, channel string, userVersion string) (r NewBuildResponse, err error) {
	path := c.MakePath("wharf/builds")

	form := url.Values{}
	form.Add("target", target)
	form.Add("channel", channel)
	if userVersion != "" {
		form.Add("user_version", userVersion)
	}

	resp, err := c.PostForm(path, form)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type BuildFileInfo struct {
	ID      int64
	Size    int64
	State   BuildFileState
	Type    BuildFileType
	SubType BuildFileSubType

	CreatedAt string `json:"created_at"`
	UpdatedAt string `json:"updated_at"`
}

type BuildInfo struct {
	ID            int64
	ParentBuildID int64 `json:"parent_build_id"`
	State         BuildState

	Files []*BuildFileInfo

	User      User
	CreatedAt string `json:"created_at"`
	UpdatedAt string `json:"updated_at"`
}

type ChannelInfo struct {
	Name string
	Tags string

	Upload  Upload
	Head    *BuildInfo `json:"head"`
	Pending *BuildInfo `json:"pending"`
}

type ListChannelsResponse struct {
	Response

	Channels map[string]ChannelInfo
}

type GetChannelResponse struct {
	Response

	Channel ChannelInfo
}

func (c *Client) ListChannels(target string) (r ListChannelsResponse, err error) {
	form := url.Values{}
	form.Add("target", target)
	path := c.MakePath("wharf/channels?%s", form.Encode())

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

func (c *Client) GetChannel(target string, channel string) (r GetChannelResponse, err error) {
	form := url.Values{}
	form.Add("target", target)
	path := c.MakePath("wharf/channels/%s?%s", channel, form.Encode())

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type BuildFileType string

const (
	BuildFileType_PATCH     BuildFileType = "patch"
	BuildFileType_ARCHIVE                 = "archive"
	BuildFileType_SIGNATURE               = "signature"
)

type BuildFileSubType string

const (
	BuildFileSubType_DEFAULT   BuildFileSubType = "default"
	BuildFileSubType_GZIP                       = "gzip"
	BuildFileSubType_OPTIMIZED                  = "optimized"
)

type UploadType string

const (
	UploadType_MULTIPART UploadType = "multipart"
	UploadType_RESUMABLE            = "resumable"
)

type BuildState string

const (
	BuildState_STARTED    BuildState = "started"
	BuildState_PROCESSING            = "processing"
	BuildState_COMPLETED             = "completed"
	BuildState_FAILED                = "failed"
)

type BuildFileState string

const (
	BuildFileState_CREATED   BuildFileState = "created"
	BuildFileState_UPLOADING                = "uploading"
	BuildFileState_UPLOADED                 = "uploaded"
	BuildFileState_FAILED                   = "failed"
)

type ListBuildFilesResponse struct {
	Response

	Files []BuildFileInfo
}

func (c *Client) ListBuildFiles(buildID int64) (r ListBuildFilesResponse, err error) {
	path := c.MakePath("wharf/builds/%d/files", buildID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type NewBuildFileResponse struct {
	Response

	File struct {
		ID           int64
		UploadURL    string            `json:"upload_url"`
		UploadParams map[string]string `json:"upload_params"`
	}
}

func (c *Client) CreateBuildFile(buildID int64, fileType BuildFileType, subType BuildFileSubType, uploadType UploadType) (r NewBuildFileResponse, err error) {
	path := c.MakePath("wharf/builds/%d/files", buildID)

	form := url.Values{}
	form.Add("type", string(fileType))
	if subType != "" {
		form.Add("sub_type", string(subType))
	}
	if uploadType != "" {
		form.Add("upload_type", string(uploadType))
	}

	resp, err := c.PostForm(path, form)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type FinalizeBuildFileResponse struct {
	Response
}

func (c *Client) FinalizeBuildFile(buildID int64, fileID int64, size int64) (r FinalizeBuildFileResponse, err error) {
	path := c.MakePath("wharf/builds/%d/files/%d", buildID, fileID)

	form := url.Values{}
	form.Add("size", fmt.Sprintf("%d", size))

	resp, err := c.PostForm(path, form)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type DownloadBuildFileResponse struct {
	Response

	URL string
}

func (c *Client) DownloadBuildFile(buildID int64, fileID int64) (reader io.ReadCloser, err error) {
	path := c.MakePath("wharf/builds/%d/files/%d/download", buildID, fileID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	var r DownloadBuildFileResponse
	err = ParseAPIResponse(&r, resp)
	if err != nil {
		return
	}

	req, err := http.NewRequest("GET", r.URL, nil)
	if err != nil {
		return
	}

	// not an API request, going directly with http's DefaultClient
	dlResp, err := http.DefaultClient.Do(req)
	if err != nil {
		return
	}

	if dlResp.StatusCode != 200 {
		err = fmt.Errorf("Can't download: %s", dlResp.Status)
		return
	}

	reader = dlResp.Body
	return
}

type BuildEventType string

const (
	BuildEvent_JOB_STARTED   BuildEventType = "job_started"
	BuildEvent_JOB_FAILED                   = "job_failed"
	BuildEvent_JOB_COMPLETED                = "job_completed"
)

type BuildEventData map[string]interface{}

type NewBuildEventResponse struct {
	Response
}

func (c *Client) CreateBuildEvent(buildID int64, eventType BuildEventType, message string, data BuildEventData) (r NewBuildEventResponse, err error) {
	path := c.MakePath("wharf/builds/%d/events", buildID)

	form := url.Values{}
	form.Add("type", string(eventType))
	form.Add("message", message)

	jsonData, err := json.Marshal(data)
	if err != nil {
		return
	}
	form.Add("data", string(jsonData))

	resp, err := c.PostForm(path, form)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

type BuildEvent struct {
	Type    BuildEventType
	Message string
	Data    BuildEventData
}

type ListBuildEventsResponse struct {
	Response

	Events []BuildEvent
}

func (c *Client) ListBuildEvents(buildID int64) (r ListBuildEventsResponse, err error) {
	path := c.MakePath("wharf/builds/%d/events", buildID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

// Helpers

func (c *Client) Get(url string) (*http.Response, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	return c.Do(req)
}

func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) {
	req, err := http.NewRequest("POST", url, strings.NewReader(data.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	return c.Do(req)
}

func (c *Client) Do(req *http.Request) (*http.Response, error) {
	if strings.HasPrefix(c.Key, "jwt:") {
		req.Header.Add("Authorization", strings.Split(c.Key, ":")[1])
	}

	var res *http.Response
	var err error

	retryPatterns := append(c.RetryPatterns, time.Millisecond)

	for _, sleepTime := range retryPatterns {
		res, err = c.HTTPClient.Do(req)
		if res.StatusCode == 503 {
			// Rate limited, try again according to patterns.
			// following https://cloud.google.com/storage/docs/json_api/v1/how-tos/upload#exp-backoff to the letter
			res.Body.Close()
			time.Sleep(sleepTime + time.Duration(rand.Int()%1000)*time.Millisecond)
		} else {
			break
		}
	}

	return res, err
}

func (c *Client) MakePath(format string, a ...interface{}) string {
	base := strings.Trim(c.BaseURL, "/")
	subPath := strings.Trim(fmt.Sprintf(format, a...), "/")

	var key string
	if strings.HasPrefix(c.Key, "jwt:") {
		key = "jwt"
	} else {
		key = c.Key
	}
	return fmt.Sprintf("%s/%s/%s", base, key, subPath)
}

func ParseAPIResponse(dst interface{}, res *http.Response) error {
	if res == nil || res.Body == nil {
		return fmt.Errorf("No response from server")
	}

	bodyReader := res.Body
	defer bodyReader.Close()

	if res.StatusCode/100 != 2 {
		return fmt.Errorf("Server returned %s for %s", res.Status, res.Request.URL.String())
	}

	err := json.NewDecoder(bodyReader).Decode(dst)
	if err != nil {
		return err
	}

	errs := reflect.Indirect(reflect.ValueOf(dst)).FieldByName("Errors")
	if errs.Len() > 0 {
		// TODO: handle other errors too
		return errors.New(errs.Index(0).String())
	}

	return nil
}
<|endoftext|>


<commit_before>package tui

import (
	"image"
	"strings"

	wordwrap "github.com/mitchellh/go-wordwrap"
)

var _ Widget = &Label{}

// Label is a widget to display read-only text.
type Label struct {
	WidgetBase

	text     string
	wordWrap bool

	// cache the result of SizeHint() (see #14)
	cacheSizeHint *image.Point

	styleName string
}

// NewLabel returns a new Label.
func NewLabel(text string) *Label {
	return &Label{
		text: text,
	}
}

// Resize changes the size of the Widget.
func (l *Label) Resize(size image.Point) {
	if l.Size() != size {
		l.cacheSizeHint = nil
	}
	l.WidgetBase.Resize(size)
}

// Draw draws the label.
func (l *Label) Draw(p *Painter) {
	lines := strings.Split(l.text, "\n")

	if l.wordWrap {
		lines = strings.Split(wordwrap.WrapString(l.text, uint(l.Size().X)), "\n")
	}

	style := "label"
	if l.styleName != "" {
		style += "." + l.styleName
	}

	p.WithStyle(style, func(p *Painter) {
		for i, line := range lines {
			p.DrawText(0, i, line)
		}
	})
}

// MinSizeHint returns the minimum size the widget is allowed to be.
func (l *Label) MinSizeHint() image.Point {
	return image.Point{1, 1}
}

// SizeHint returns the recommended size for the label.
func (l *Label) SizeHint() image.Point {
	if l.cacheSizeHint != nil {
		return *l.cacheSizeHint
	}
	var max int
	lines := strings.Split(l.text, "\n")
	if l.wordWrap {
		lines = strings.Split(wordwrap.WrapString(l.text, uint(l.Size().X)), "\n")
	}
	for _, line := range lines {
		if w := stringWidth(line); w > max {
			max = w
		}
	}
	sizeHint := image.Point{max, l.heightForWidth(max)}
	l.cacheSizeHint = &sizeHint
	return sizeHint
}

func (l *Label) heightForWidth(w int) int {
	return len(strings.Split(wordwrap.WrapString(l.text, uint(w)), "\n"))
}

// Text returns the text content of the label.
func (l *Label) Text() string {
	return l.text
}

// SetText sets the text content of the label.
func (l *Label) SetText(text string) {
	l.cacheSizeHint = nil
	l.text = text
}

// SetWordWrap sets whether text content should be wrapped.
func (l *Label) SetWordWrap(enabled bool) {
	l.wordWrap = enabled
}

// SetStyleName sets the identifier used for custom styling.
func (l *Label) SetStyleName(style string) {
	l.styleName = style
}
<commit_msg>Factor wrappedText and fix tests.<commit_after>package tui

import (
	"image"
	"strings"

	wordwrap "github.com/mitchellh/go-wordwrap"
)

var _ Widget = &Label{}

// Label is a widget to display read-only text.
type Label struct {
	WidgetBase

	text     string
	wordWrap bool

	// cache the result of SizeHint() (see #14)
	cacheSizeHint *image.Point

	styleName string
}

// NewLabel returns a new Label.
func NewLabel(text string) *Label {
	return &Label{
		text: text,
	}
}

// Resize changes the size of the Widget.
func (l *Label) Resize(size image.Point) {
	if l.Size() != size {
		l.cacheSizeHint = nil
	}
	l.WidgetBase.Resize(size)
}

// Draw draws the label.
func (l *Label) Draw(p *Painter) {
	lines := l.lines()

	style := "label"
	if l.styleName != "" {
		style += "." + l.styleName
	}

	p.WithStyle(style, func(p *Painter) {
		for i, line := range lines {
			p.DrawText(0, i, line)
		}
	})
}

// MinSizeHint returns the minimum size the widget is allowed to be.
func (l *Label) MinSizeHint() image.Point {
	return image.Point{1, 1}
}

// SizeHint returns the recommended size for the label.
func (l *Label) SizeHint() image.Point {
	if l.cacheSizeHint != nil {
		return *l.cacheSizeHint
	}
	var max int
	lines := l.lines()
	for _, line := range lines {
		if w := stringWidth(line); w > max {
			max = w
		}
	}
	sizeHint := image.Point{max, len(lines)}
	l.cacheSizeHint = &sizeHint
	return sizeHint
}

func (l *Label) lines() []string {
	txt := l.text
	if l.wordWrap {
		txt = wordwrap.WrapString(l.text, uint(l.Size().X))
	}
	return strings.Split(txt, "\n")
}

// Text returns the text content of the label.
func (l *Label) Text() string {
	return l.text
}

// SetText sets the text content of the label.
func (l *Label) SetText(text string) {
	l.cacheSizeHint = nil
	l.text = text
}

// SetWordWrap sets whether text content should be wrapped.
func (l *Label) SetWordWrap(enabled bool) {
	l.wordWrap = enabled
}

// SetStyleName sets the identifier used for custom styling.
func (l *Label) SetStyleName(style string) {
	l.styleName = style
}
<|endoftext|>


<commit_before><commit_msg>fix for newHttpClient is not idempotency<commit_after><|endoftext|>