<commit_before>package consumer

import (
	"bytes"
	"compress/zlib"
	"encoding/base64"
	"errors"
	"fmt"
	"github.com/maxposter/rabbitmq-cli-consumer/command"
	"github.com/maxposter/rabbitmq-cli-consumer/config"
	"github.com/streadway/amqp"
	"log"
	"net/url"
)

type Consumer struct {
	Channel     *amqp.Channel
	Connection  *amqp.Connection
	Queue       string
	Factory     *command.CommandFactory
	ErrLogger   *log.Logger
	InfLogger   *log.Logger
	Executer    *command.CommandExecuter
	Compression bool
}

func (c *Consumer) Consume() {
	c.InfLogger.Println("Registering consumer... ")
	msgs, err := c.Channel.Consume(c.Queue, "", false, false, false, false, nil)
	if err != nil {
		c.ErrLogger.Fatalf("Failed to register a consumer: %s", err)
	}
	c.InfLogger.Println("Succeeded registering consumer.")

	defer c.Connection.Close()
	defer c.Channel.Close()

	forever := make(chan bool)

	go func() {
		for d := range msgs {
			input := d.Body
			if c.Compression {
				var b bytes.Buffer
				w, err := zlib.NewWriterLevel(&b, zlib.BestCompression)
				if err != nil {
					c.ErrLogger.Println("Could not create zlib handler")
					d.Nack(true, true)
				}
				c.InfLogger.Println("Compressed message")
				w.Write(input)
				w.Close()

				input = b.Bytes()
			}

			cmd := c.Factory.Create(base64.StdEncoding.EncodeToString(input))
			if c.Executer.Execute(cmd) {
				d.Ack(true)
			} else {
				d.Nack(true, true)
			}
		}
	}()
	c.InfLogger.Println("Waiting for messages...")
	<-forever
}

func New(cfg *config.Config, factory *command.CommandFactory, errLogger, infLogger *log.Logger) (*Consumer, error) {
	uri := fmt.Sprintf(
		"amqp://%s:%s@%s:%s%s",
		url.QueryEscape(cfg.RabbitMq.Username),
		url.QueryEscape(cfg.RabbitMq.Password),
		cfg.RabbitMq.Host,
		cfg.RabbitMq.Port,
		cfg.RabbitMq.Vhost,
	)

	infLogger.Println("Connecting RabbitMQ...")
	conn, err := amqp.Dial(uri)
	if nil != err {
		return nil, errors.New(fmt.Sprintf("Failed connecting RabbitMQ: %s", err.Error()))
	}
	infLogger.Println("Connected.")

	infLogger.Println("Opening channel...")
	ch, err := conn.Channel()
	if nil != err {
		return nil, errors.New(fmt.Sprintf("Failed to open a channel: %s", err.Error()))
	}
	infLogger.Println("Done.")

	infLogger.Println("Setting QoS... ")
	// Attempt to preserve BC here
	if cfg.Prefetch.Count == 0 {
		cfg.Prefetch.Count = 3
	}
	if err := ch.Qos(cfg.Prefetch.Count, 0, cfg.Prefetch.Global); err != nil {
		return nil, errors.New(fmt.Sprintf("Failed to set QoS: %s", err.Error()))
	}
	infLogger.Println("Succeeded setting QoS.")

	infLogger.Printf("Declaring queue \"%s\"...", cfg.RabbitMq.Queue)
	_, err = ch.QueueDeclare(cfg.RabbitMq.Queue, true, false, false, false, nil)

	if nil != err {
		return nil, errors.New(fmt.Sprintf("Failed to declare queue: %s", err.Error()))
	}

	// Check for missing exchange settings to preserve BC
	if "" == cfg.Exchange.Name && "" == cfg.Exchange.Type && !cfg.Exchange.Durable && !cfg.Exchange.Autodelete {
		cfg.Exchange.Type = "direct"
	}

	// Empty Exchange name means default, no need to declare
	if "" != cfg.Exchange.Name {
		infLogger.Printf("Declaring exchange \"%s\"...", cfg.Exchange.Name)

		argsTable := amqp.Table{};
		if "" != cfg.Exchange.XDelayedType {
			argsTable["x-delayed-type"] = cfg.Exchange.XDelayedType;
		}
		err = ch.ExchangeDeclare(cfg.Exchange.Name, cfg.Exchange.Type, cfg.Exchange.Durable, cfg.Exchange.Autodelete, false, false, argsTable)

		if nil != err {
			return nil, errors.New(fmt.Sprintf("Failed to declare exchange: %s", err.Error()))
		}

		// Bind queue
		infLogger.Printf("Binding queue \"%s\" to exchange \"%s\"...", cfg.RabbitMq.Queue, cfg.Exchange.Name)
		err = ch.QueueBind(cfg.RabbitMq.Queue, "", cfg.Exchange.Name, false, nil)

		if nil != err {
			return nil, errors.New(fmt.Sprintf("Failed to bind queue to exchange: %s", err.Error()))
		}
	}

	return &Consumer{
		Channel:     ch,
		Connection:  conn,
		Queue:       cfg.RabbitMq.Queue,
		Factory:     factory,
		ErrLogger:   errLogger,
		InfLogger:   infLogger,
		Executer:    command.New(errLogger, infLogger),
		Compression: cfg.RabbitMq.Compression,
	}, nil
}
<commit_msg>Catch terminate signals<commit_after>package consumer

import (
	"bytes"
	"compress/zlib"
	"encoding/base64"
	"errors"
	"fmt"
	"os"
    "os/signal"
    "syscall"
	"github.com/maxposter/rabbitmq-cli-consumer/command"
	"github.com/maxposter/rabbitmq-cli-consumer/config"
	"github.com/streadway/amqp"
	"log"
	"net/url"
)

type Consumer struct {
	Channel     *amqp.Channel
	Connection  *amqp.Connection
	Queue       string
	Factory     *command.CommandFactory
	ErrLogger   *log.Logger
	InfLogger   *log.Logger
	Executer    *command.CommandExecuter
	Compression bool
}

func (c *Consumer) Consume() {
	c.InfLogger.Println("Registering consumer... ")
	msgs, err := c.Channel.Consume(c.Queue, "", false, false, false, false, nil)
	if err != nil {
		c.ErrLogger.Fatalf("Failed to register a consumer: %s", err)
	}
	c.InfLogger.Println("Succeeded registering consumer.")

	defer c.Connection.Close()
	defer c.Channel.Close()

	needTerminate := false
	inProgress := false

	termanateChan := make(chan os.Signal, 1)
    signal.Notify(termanateChan, os.Interrupt)
    signal.Notify(termanateChan, syscall.SIGTERM)

	var termanate = func() {
		c.InfLogger.Println("Terminated.")
		os.Exit(1)
	}

    go func() {
        <-termanateChan
		needTerminate = true;
		if inProgress {
			c.InfLogger.Println("Waiting for correct conusmer termanation...")
			needTerminate = true;
		} else {
			termanate()
		}
    }()

	c.InfLogger.Println("Waiting for messages...")
	for d := range msgs {
		if needTerminate {
			termanate()
		}
		inProgress = true;

		input := d.Body
		if c.Compression {
			var b bytes.Buffer
			w, err := zlib.NewWriterLevel(&b, zlib.BestCompression)
			if err != nil {
				c.ErrLogger.Println("Could not create zlib handler")
				d.Nack(true, true)
			}
			c.InfLogger.Println("Compressed message")
			w.Write(input)
			w.Close()

			input = b.Bytes()
		}

		cmd := c.Factory.Create(base64.StdEncoding.EncodeToString(input))
		if c.Executer.Execute(cmd) {
			d.Ack(true)
		} else {
			d.Nack(true, true)
		}

		inProgress = false;
	}
}

func New(cfg *config.Config, factory *command.CommandFactory, errLogger, infLogger *log.Logger) (*Consumer, error) {
	uri := fmt.Sprintf(
		"amqp://%s:%s@%s:%s%s",
		url.QueryEscape(cfg.RabbitMq.Username),
		url.QueryEscape(cfg.RabbitMq.Password),
		cfg.RabbitMq.Host,
		cfg.RabbitMq.Port,
		cfg.RabbitMq.Vhost,
	)

	infLogger.Println("Connecting RabbitMQ...")
	conn, err := amqp.Dial(uri)
	if nil != err {
		return nil, errors.New(fmt.Sprintf("Failed connecting RabbitMQ: %s", err.Error()))
	}
	infLogger.Println("Connected.")

	infLogger.Println("Opening channel...")
	ch, err := conn.Channel()
	if nil != err {
		return nil, errors.New(fmt.Sprintf("Failed to open a channel: %s", err.Error()))
	}
	infLogger.Println("Done.")

	infLogger.Println("Setting QoS... ")
	// Attempt to preserve BC here
	if cfg.Prefetch.Count == 0 {
		cfg.Prefetch.Count = 3
	}
	if err := ch.Qos(cfg.Prefetch.Count, 0, cfg.Prefetch.Global); err != nil {
		return nil, errors.New(fmt.Sprintf("Failed to set QoS: %s", err.Error()))
	}
	infLogger.Println("Succeeded setting QoS.")

	infLogger.Printf("Declaring queue \"%s\"...", cfg.RabbitMq.Queue)
	_, err = ch.QueueDeclare(cfg.RabbitMq.Queue, true, false, false, false, nil)

	if nil != err {
		return nil, errors.New(fmt.Sprintf("Failed to declare queue: %s", err.Error()))
	}

	// Check for missing exchange settings to preserve BC
	if "" == cfg.Exchange.Name && "" == cfg.Exchange.Type && !cfg.Exchange.Durable && !cfg.Exchange.Autodelete {
		cfg.Exchange.Type = "direct"
	}

	// Empty Exchange name means default, no need to declare
	if "" != cfg.Exchange.Name {
		infLogger.Printf("Declaring exchange \"%s\"...", cfg.Exchange.Name)

		argsTable := amqp.Table{};
		if "" != cfg.Exchange.XDelayedType {
			argsTable["x-delayed-type"] = cfg.Exchange.XDelayedType;
		}
		err = ch.ExchangeDeclare(cfg.Exchange.Name, cfg.Exchange.Type, cfg.Exchange.Durable, cfg.Exchange.Autodelete, false, false, argsTable)

		if nil != err {
			return nil, errors.New(fmt.Sprintf("Failed to declare exchange: %s", err.Error()))
		}

		// Bind queue
		infLogger.Printf("Binding queue \"%s\" to exchange \"%s\"...", cfg.RabbitMq.Queue, cfg.Exchange.Name)
		err = ch.QueueBind(cfg.RabbitMq.Queue, "", cfg.Exchange.Name, false, nil)

		if nil != err {
			return nil, errors.New(fmt.Sprintf("Failed to bind queue to exchange: %s", err.Error()))
		}
	}

	return &Consumer{
		Channel:     ch,
		Connection:  conn,
		Queue:       cfg.RabbitMq.Queue,
		Factory:     factory,
		ErrLogger:   errLogger,
		InfLogger:   infLogger,
		Executer:    command.New(errLogger, infLogger),
		Compression: cfg.RabbitMq.Compression,
	}, nil
}
<|endoftext|>


<commit_before>/*
 * Funkensturm, a versatile DNS proxy
 * Miek Gieben <miek@miek.nl> (c) 2011
 * GPLv2
 */

package main

/* TODO log package */
import (
	"os"
	"flag"
	"net"
	"fmt"
	"dns"
	"os/signal"
	"strings"
)

// Define a responder that takes care of the incoming queries.
type server dns.Server

// Define a slice of channels for the resolver for sending the queries somewhere else.
var qr []*dns.Resolver

// The configuration of Funkensturm
var f *Funkensturm

// Verbose flag
var verbose *bool

// Where does the packet come from?
// IN: initial packet received by the Responder
// any modifications here will reflect what kind of
// pkt is sent through.
// OUT: pkt as received back from a server. Modifications here will reflect
// how the packet is send back to the original requester.
const (
	IN  = iota // set when receiving a packet
	OUT        // set when sending a packet

	OR  // chain match functions with logical 'or'
	AND // chain match functions with logical 'and'
)

// A Match function is used on a DNS packet and
// returns (a possibly modified) DNS packet. It should
// return true when the packets matches the criteria set in
// the function.
// Op is used in chaining Match-functions together
type Match struct {
	Op   int // boolean op: OR, AND
	Func func(*dns.Msg, int) (*dns.Msg, bool)
}

// An action is something that is done with a packet. Funkensturm
// does not impose any restriction on what this can be, except that
// is must remain a valid DNS packet.
type Action struct {
	Func func(*dns.Msg, bool) *dns.Msg
}

// A complete config for Funkensturm. All matches in the Matches slice are
// chained together: incoming dns.Msg -> Match[0] -> dns.Msg -> Match[1] -> dns.Msg -> ...
// The dns.Msg output of Match[n] is the input for Match[n+1].
//
// The final outcome (does a packet match or not?) is calculated as follows:
// true Match[0].Op Match[0].Func() Match[1].Op Match[1].Func() ...
// The result of this matching is given to the action function(s). They can then
// decide what to do with a packet in the 'true' and in the 'false' case.
type Funkensturm struct {
	Setup   func() bool // Inital setup (for extra resolvers, or loading keys, or ...)
	Matches []Match     // Match- and modify functions
	Actions []Action    // What to do with the packets
}

func doFunkensturm(i []byte) ([]byte, os.Error) {
	pkt := new(dns.Msg)
	if !pkt.Unpack(i) {
		return nil, &dns.Error{Error: "Unpacking packet failed"}
	}
	if *verbose {
		fmt.Printf(">>>>>> ORIGINAL INCOMING\n")
		fmt.Printf("%v", pkt)
		fmt.Printf("<<<<<< ORIGINAL INCOMING\n")
	}
	// No matter what, we refuse to answer requests with the response bit set.
	if pkt.MsgHdr.Response == true {
		return nil, &dns.Error{Error: "Response bit set, not replying"}
	}

	// Loop through the Match* functions and decide what to do
	// Note the packet can be changed by these function, this
	// change is cumulative.
	ok, ok1 := true, true
	pkt1 := pkt
	for _, m := range f.Matches {
		pkt1, ok1 = m.Func(pkt1, IN)
		switch m.Op {
		case AND:
			ok = ok && ok1
		case OR:
			ok = ok || ok1
		}
	}
	if *verbose {
		fmt.Printf(">>>>>> MODIFIED INCOMING\n")
		fmt.Printf("%v", pkt1)
		fmt.Printf("<<<<<< MODIFIED INCOMING\n")
	}

	// Loop through the Actions.Func* and do something with the
	// packet. Note there can only be one returned packet.
	// We use 'ok' to signal what the above match did: true or false
	var resultpkt *dns.Msg
	for _, a := range f.Actions {
		resultpkt = a.Func(pkt1, ok)
	}

	if *verbose {
		fmt.Printf(">>>>>> ORIGINAL OUTGOING\n")
		fmt.Printf("%v", resultpkt)
		fmt.Printf("<<<<<< ORIGINAL OUTGOING\n")
	}

	// loop again for matching, but now with OUT, this is done
	// for some last minute packet changing. Note the boolean return
	// code isn't used any more, i.e No more actions are allowed
	// anymore
	pkt1 = resultpkt
	for _, m := range f.Matches {
		pkt1, _ = m.Func(pkt1, OUT)
	}

	if pkt1 == nil {
		// don't need to send something back
		return nil, nil
	}

	if *verbose {
		fmt.Printf(">>>>>> MODIFIED OUTGOING\n")
		fmt.Printf("%v", pkt1)
		fmt.Printf("<<<<<< MODIFIED OUTGOING\n")
	}

	out, ok1 := pkt1.Pack()
	if !ok1 {
		return nil, &dns.Error{Error: "Packing packet failed"}
	}
	// Some final byte changing function here?
	return out, nil
}

func (s *server) ReplyUDP(c *net.UDPConn, a net.Addr, i []byte) {
	out, err := doFunkensturm(i)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err.String())
		return
	}

	if out != nil {
		dns.SendUDP(out, c, a)
	}
	// nothing is send back
}

func (s *server) ReplyTCP(c *net.TCPConn, a net.Addr, i []byte) {
	out, err := doFunkensturm(i)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err.String())
		return
	}

	if out != nil {
		dns.SendTCP(out, c, a)
	}
	// nothing is send back
}

// split 127.0.0.1:53 into components
// TODO  IPv6
func splitAddrPort(s string) (a, p string) {
       items := strings.Split(s, ":", 2)
       a = items[0]
       p = items[1]
       return
}

func main() {
	var sserver *string = flag.String("sserver", "127.0.0.1:8053", "Set the listener address")
	var rserver *string = flag.String("rserver", "127.0.0.1:53", "Remote server address(es)")
	verbose = flag.Bool("verbose", false, "Print packet as it flows through") // verbose needs to be global
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	resolvers := strings.Split(*rserver, ",", -1)
	qr = make([]*dns.Resolver, len(resolvers))
	for i, ra := range resolvers {
		addr, port := splitAddrPort(ra)
		r := new(dns.Resolver)
		r.Servers = []string{addr}
		r.Port = port
		qr[i] = r
	}

	f = funkensturm()
	ok := f.Setup()
	if !ok {
		fmt.Fprintf(os.Stderr, "Setup failed")
		return
	}

	// The server
	var srv *server
	quit := make(chan bool)
        go dns.ListenAndServe(*sserver, srv, quit)

forever:
	for {
		// Wait for a signal to stop
		select {
		case <-signal.Incoming:
			println("Signal received, stopping")
			quit <- true
			break forever
		}
	}
	close(quit)
}
<commit_msg>helper function: less code needed<commit_after>/*
 * Funkensturm, a versatile DNS proxy
 * Miek Gieben <miek@miek.nl> (c) 2011
 * GPLv2
 */

package main

/* TODO log package */
import (
	"os"
	"flag"
	"net"
	"fmt"
	"dns"
	"os/signal"
	"strings"
)

// Define a responder that takes care of the incoming queries.
type server dns.Server

// Define a slice of channels for the resolver for sending the queries somewhere else.
var qr []*dns.Resolver

// The configuration of Funkensturm
var f *Funkensturm

// Verbose flag
var verbose *bool

// Where does the packet come from?
// IN: initial packet received by the Responder
// any modifications here will reflect what kind of
// pkt is sent through.
// OUT: pkt as received back from a server. Modifications here will reflect
// how the packet is send back to the original requester.
const (
	IN  = iota // set when receiving a packet
	OUT        // set when sending a packet

	OR  // chain match functions with logical 'or'
	AND // chain match functions with logical 'and'
)

// A Match function is used on a DNS packet and
// returns (a possibly modified) DNS packet. It should
// return true when the packets matches the criteria set in
// the function.
// Op is used in chaining Match-functions together
type Match struct {
	Op   int // boolean op: OR, AND
	Func func(*dns.Msg, int) (*dns.Msg, bool)
}

// An action is something that is done with a packet. Funkensturm
// does not impose any restriction on what this can be, except that
// is must remain a valid DNS packet.
type Action struct {
	Func func(*dns.Msg, bool) *dns.Msg
}

// A complete config for Funkensturm. All matches in the Matches slice are
// chained together: incoming dns.Msg -> Match[0] -> dns.Msg -> Match[1] -> dns.Msg -> ...
// The dns.Msg output of Match[n] is the input for Match[n+1].
//
// The final outcome (does a packet match or not?) is calculated as follows:
// true Match[0].Op Match[0].Func() Match[1].Op Match[1].Func() ...
// The result of this matching is given to the action function(s). They can then
// decide what to do with a packet in the 'true' and in the 'false' case.
type Funkensturm struct {
	Setup   func() bool // Inital setup (for extra resolvers, or loading keys, or ...)
	Matches []Match     // Match- and modify functions
	Actions []Action    // What to do with the packets
}

func verboseprint(i *dns.Msg, msg string) {
	fmt.Printf(">>>>>> %s\n", msg)
	fmt.Printf("%v", i)
	fmt.Printf("<<<<<< %s\n\n", msg)
}

func doFunkensturm(i []byte) ([]byte, os.Error) {
	pkt := new(dns.Msg)
	if !pkt.Unpack(i) {
		return nil, &dns.Error{Error: "Unpacking packet failed"}
	}
	if *verbose {
		verboseprint(pkt, "ORIGINAL INCOMING")
	}
	// No matter what, we refuse to answer requests with the response bit set.
	if pkt.MsgHdr.Response == true {
		return nil, &dns.Error{Error: "Response bit set, not replying"}
	}

	// Loop through the Match* functions and decide what to do
	// Note the packet can be changed by these function, this
	// change is cumulative.
	ok, ok1 := true, true
	pkt1 := pkt
	for _, m := range f.Matches {
		pkt1, ok1 = m.Func(pkt1, IN)
		switch m.Op {
		case AND:
			ok = ok && ok1
		case OR:
			ok = ok || ok1
		}
	}
	if *verbose {
		verboseprint(pkt1, "MODIFIED INCOMING")
	}

	// Loop through the Actions.Func* and do something with the
	// packet. Note there can only be one returned packet.
	// We use 'ok' to signal what the above match did: true or false
	var resultpkt *dns.Msg
	for _, a := range f.Actions {
		resultpkt = a.Func(pkt1, ok)
	}

	if *verbose {
		verboseprint(resultpkt, "ORIGINAL OUTGOING")
	}

	// loop again for matching, but now with OUT, this is done
	// for some last minute packet changing. Note the boolean return
	// code isn't used any more, i.e No more actions are allowed
	// anymore
	pkt1 = resultpkt
	for _, m := range f.Matches {
		pkt1, _ = m.Func(pkt1, OUT)
	}

	if pkt1 == nil {
		// don't need to send something back
		return nil, nil
	}

	if *verbose {
		verboseprint(pkt1, "MODIFIED OUTGOING")
		fmt.Printf("-----------------------------------\n\n")
	}

	out, ok1 := pkt1.Pack()
	if !ok1 {
		return nil, &dns.Error{Error: "Packing packet failed"}
	}
	// Some final byte changing function here?
	return out, nil
}

func (s *server) ReplyUDP(c *net.UDPConn, a net.Addr, i []byte) {
	out, err := doFunkensturm(i)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err.String())
		return
	}

	if out != nil {
		dns.SendUDP(out, c, a)
	}
	// nothing is send back
}

func (s *server) ReplyTCP(c *net.TCPConn, a net.Addr, i []byte) {
	out, err := doFunkensturm(i)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err.String())
		return
	}

	if out != nil {
		dns.SendTCP(out, c, a)
	}
	// nothing is send back
}

// split 127.0.0.1:53 into components
// TODO  IPv6
func splitAddrPort(s string) (a, p string) {
	items := strings.Split(s, ":", 2)
	a = items[0]
	p = items[1]
	return
}

func main() {
	var sserver *string = flag.String("sserver", "127.0.0.1:8053", "Set the listener address")
	var rserver *string = flag.String("rserver", "127.0.0.1:53", "Remote server address(es)")
	verbose = flag.Bool("verbose", false, "Print packet as it flows through") // verbose needs to be global
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s\n", os.Args[0])
		flag.PrintDefaults()
	}
	flag.Parse()

	resolvers := strings.Split(*rserver, ",", -1)
	qr = make([]*dns.Resolver, len(resolvers))
	for i, ra := range resolvers {
		addr, port := splitAddrPort(ra)
		r := new(dns.Resolver)
		r.Servers = []string{addr}
		r.Port = port
		qr[i] = r
	}

	f = funkensturm()
	ok := f.Setup()
	if !ok {
		fmt.Fprintf(os.Stderr, "Setup failed")
		return
	}

	// The server
	var srv *server
	quit := make(chan bool)
	go dns.ListenAndServe(*sserver, srv, quit)

forever:
	for {
		// Wait for a signal to stop
		select {
		case <-signal.Incoming:
			println("Signal received, stopping")
			quit <- true
			break forever
		}
	}
	close(quit)
}
<|endoftext|>


<commit_before>package devgorant

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	API         = "https://www.devrant.io/api"
	APP_VERSION = 3
)

type Client struct {
}

// Fetches rants
func (c *Client) Rants(sort string, limit int, skip int) ([]RantModel, error) {
	if sort == "" {
		sort = "algo"
	} else if (sort != "algo") && (sort != "recent") && (sort != "top") {
		return nil, errors.New("Invalid string in sort method")
	}
	if limit <= 0 {
		limit = 50
	}

	url := fmt.Sprintf("%s/devrant/rants?sort=%s&limit=%d&skip=%d&app=%d",
		API, sort, limit, skip, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	var data RantsResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rants, nil
}

// Fetches a rant and its comments given a valid rant id
func (c *Client) Rant(rantId int) (RantModel, []CommentModel, error) {
	url := fmt.Sprintf("%s/devrant/rants/%d?app=%d", API, rantId, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return RantModel{}, nil, err
	}
	var data RantResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rant, data.Comments, nil
}

// Fetches ranter's profile data
func (c *Client) Profile(username string) (UserModel, ContentModel, error) {
	userId, err := getUserId(username)
	if err != nil {
		return UserModel{}, ContentModel{}, err
	}
	url := fmt.Sprintf("%s/users/%d?app=%d", API, userId, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return UserModel{}, ContentModel{}, err
	}
	var data UserResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Profile, data.Profile.Content.Content, nil
}

// Search for rants matching the search term
func (c *Client) Search(term string) ([]RantModel, error) {
	url := fmt.Sprintf("%s/devrant/search?term=%s&app=%d", API, term, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	var data SearchResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rants, nil
}

// Returns a random rant
func (c *Client) Surprise() (RantModel, error) {
	url := fmt.Sprintf("%s/devrant/rants/surprise?app=%d", API, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return RantModel{}, err
	}
	var data RantResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rant, nil
}

// Returns the rants tagged for 'weekly'
func (c *Client) WeeklyRants() ([]RantModel, error) {
	url := fmt.Sprintf("%s/devrant/weekly-rants?app=%d", API, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	var data RantsResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rants, nil
}

// Fetches the userId given a valid username
func getUserId(username string) (int, error) {
	url := fmt.Sprintf("%s/get-user-id?username=%s&app=%d",
		API, username, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return 0, err
	}
	var data GetUserIdResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.UserId, nil
}

func New() *Client {
	return new(Client)
}
<commit_msg>Make app paths as string constants<commit_after>package devgorant

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	API           = "https://www.devrant.io/api"
	APP_VERSION   = 3
	RANTS_PATH    = "%s/devrant/rants?sort=%s&limit=%d&skip=%d&app=%d"
	RANT_PATH     = "%s/devrant/rants/%d?app=%d"
	USER_PATH     = "%s/users/%d?app=%d"
	USER_ID_PATH  = "%s/get-user-id?username=%s&app=%d"
	SEARCH_PATH   = "%s/devrant/search?term=%s&app=%d"
	SURPRISE_PATH = "%s/devrant/rants/surprise?app=%d"
	WEEKLY_PATH   = "%s/devrant/weekly-rants?app=%d"
)

type Client struct {
}

// Fetches rants
func (c *Client) Rants(sort string, limit int, skip int) ([]RantModel, error) {
	if sort == "" {
		sort = "algo"
	} else if (sort != "algo") && (sort != "recent") && (sort != "top") {
		return nil, errors.New("Invalid string in sort method")
	}
	if limit <= 0 {
		limit = 50
	}

	url := fmt.Sprintf(RANTS_PATH, API, sort, limit, skip, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	var data RantsResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rants, nil
}

// Fetches a rant and its comments given a valid rant id
func (c *Client) Rant(rantId int) (RantModel, []CommentModel, error) {
	url := fmt.Sprintf(RANT_PATH, API, rantId, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return RantModel{}, nil, err
	}
	var data RantResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rant, data.Comments, nil
}

// Fetches ranter's profile data
func (c *Client) Profile(username string) (UserModel, ContentModel, error) {
	userId, err := getUserId(username)
	if err != nil {
		return UserModel{}, ContentModel{}, err
	}
	url := fmt.Sprintf(USER_PATH, API, userId, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return UserModel{}, ContentModel{}, err
	}
	var data UserResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Profile, data.Profile.Content.Content, nil
}

// Search for rants matching the search term
func (c *Client) Search(term string) ([]RantModel, error) {
	url := fmt.Sprintf(SEARCH_PATH, API, term, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	var data SearchResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rants, nil
}

// Returns a random rant
func (c *Client) Surprise() (RantModel, error) {
	url := fmt.Sprintf(SURPRISE_PATH, API, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return RantModel{}, err
	}
	var data RantResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rant, nil
}

// Returns the rants tagged for 'weekly'
func (c *Client) WeeklyRants() ([]RantModel, error) {
	url := fmt.Sprintf(WEEKLY_PATH, API, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	var data RantsResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.Rants, nil
}

// Fetches the userId given a valid username
func getUserId(username string) (int, error) {
	url := fmt.Sprintf(USER_ID_PATH, API, username, APP_VERSION)
	res, err := http.Get(url)
	if err != nil {
		return 0, err
	}
	var data GetUserIdResponse
	json.NewDecoder(res.Body).Decode(&data)
	return data.UserId, nil
}

func New() *Client {
	return new(Client)
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 PolyFloyd
 */

package main

import (
	"fmt"
	"math"
	"runtime"
	gl     "github.com/polyfloyd/go-gl"
	glfw   "github.com/go-gl/glfw3"
	mathgl "github.com/go-gl/mathgl/mgl32"
)

type Display struct {
	Buffer  []float32
	HideOff bool

	cubeHeight  int
	cubeLength  int
	cubeWidth   int

	camRot  mathgl.Quat
	camZoom float32

	frontBuffer       []float32
	shader            gl.Program
	shouldSwapBuffers bool
	voxelBuffer       gl.Buffer
	voxelLen          int
	win               *glfw.Window
}

func NewDisplay(w, h, l int) *Display {
	disp := &Display{
		cubeHeight:  h,
		cubeLength:  l,
		cubeWidth:   w,
		Buffer:      make([]float32, w*h*l * 3),
		frontBuffer: make([]float32, w*h*l * 3),
	}
	disp.ResetView()
	return disp
}

func (disp *Display) Start() {
	runtime.LockOSThread()
	runtime.GOMAXPROCS(runtime.NumCPU())

	for i := 0; i < len(disp.frontBuffer); i += 3 {
		disp.frontBuffer[i + 0] = 0.0
		disp.frontBuffer[i + 1] = 0.4
		disp.frontBuffer[i + 2] = 1.0
	}

	if err := disp.init(); err != nil {
		panic(err)
	}

	for !disp.win.ShouldClose() {
		if (disp.shouldSwapBuffers) {
			disp.frontBuffer, disp.Buffer = disp.Buffer, disp.frontBuffer
			disp.shouldSwapBuffers = false
		}
		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

		disp.render()

		disp.win.SwapBuffers()
		glfw.PollEvents()
	}
	disp.win.Destroy()
	runtime.UnlockOSThread()
}

func (disp *Display) render() {
	uniformColor := disp.shader.GetUniformLocation("color_led")
	uniformMVP   := disp.shader.GetUniformLocation("mat_mvp")

	projection := mathgl.Perspective(
		UI_FOVY,
		func(w, h int) float32 {
			return float32(w) / float32(h)
		}(disp.win.GetSize()),
		UI_ZNEAR,
		UI_ZFAR,
	)
	center := mathgl.Translate3D(
		-(UI_SPACING*float32(disp.cubeWidth)/2  - UI_SPACING/2),
		-(UI_SPACING*float32(disp.cubeHeight)/2 - UI_SPACING/2),
		-(UI_SPACING*float32(disp.cubeLength)/2 - UI_SPACING/2),
	)
	view := func() mathgl.Mat4 {
		m := mathgl.Ident4()
		m = m.Mul4(mathgl.Translate3D(0, 0, disp.camZoom))
		m = m.Mul4(disp.camRot.Mat4())
		return m
	}()

	for x := 0; x < disp.cubeWidth; x++ {
		for y := 0; y < disp.cubeHeight; y++ {
			for z := 0; z < disp.cubeLength; z++ {
				i := x*disp.cubeHeight*disp.cubeLength + y*disp.cubeLength + z

				r := disp.frontBuffer[i*3 + 0]
				g := disp.frontBuffer[i*3 + 1]
				b := disp.frontBuffer[i*3 + 2]
				if disp.HideOff && (r==0 && g==0 && b==0) {
					continue
				}

				model := mathgl.Translate3D(
					float32(x) * UI_SPACING,
					float32(y) * UI_SPACING,
					float32(z) * UI_SPACING,
				).Mul4(center);

				mvp := projection.Mul4(view).Mul4(model)
				uniformMVP.UniformMatrix4f(false, (*[16]float32)(&mvp))
				uniformColor.Uniform3f(r, g, b)
				gl.DrawArrays(gl.TRIANGLES, 0, disp.voxelLen);
			}
		}
	}
}

func (disp *Display) init() error {
	//
	// Initialize GLFW and create a window
	//
	if !glfw.Init() {
		panic("Can't init GLFW!")
	}
	var err error
	disp.win, err = glfw.CreateWindow(UI_WIN_W, UI_WIN_H, INFO, nil, nil)
	if err != nil {
		panic(err)
	}
	disp.win.MakeContextCurrent()
	resize := func(w, h int) { gl.Viewport(0, 0, w, h) }
	disp.win.SetSizeCallback(func(win *glfw.Window, w, h int) {
		resize(w, h)
	})
	resize(disp.win.GetSize())
	glfw.SwapInterval(1)

	//
	// Initialize user input
	//
	var dragButtonDown bool
	var mousePosLastX float64
	var mousePosLastY float64
	disp.win.SetCursorPositionCallback(func(_ *glfw.Window, x, y float64) {
		deltaX := x - mousePosLastX
		deltaY := y - mousePosLastY
		mousePosLastX = x
		mousePosLastY = y
		if (dragButtonDown) {
			disp.camRot = mathgl.QuatRotate(float32(deltaX) / UI_DRAGDIV, mathgl.Vec3{0, 1, 0}).Mul(disp.camRot)
			disp.camRot = mathgl.QuatRotate(float32(deltaY) / UI_DRAGDIV, mathgl.Vec3{1, 0, 0}).Mul(disp.camRot)
		}
	})
	disp.win.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton,
		action glfw.Action, mods glfw.ModifierKey) {
		dragButtonDown = action == glfw.Press && button == glfw.MouseButtonLeft
	})
	disp.win.SetScrollCallback(func(_ *glfw.Window, dx, dy float64) {
		disp.camZoom += float32(dy) * UI_ZOOMACCEL
	})
	disp.win.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, scancode int,
		action glfw.Action, mods glfw.ModifierKey) {
		if (action != glfw.Release) {
			switch(key) {
			case glfw.KeyS: disp.HideOff = !disp.HideOff
			case glfw.KeyR: disp.ResetView()
			}
		}
	})

	//
	// Initialize OpenGL
	//
	if err := gl.Init(); err != nil { return err }
	gl.Enable(gl.DEPTH_TEST)
	gl.ClearColor(0.12, 0.12, 0.12, 1.0)

	//
	// Compile the voxel shader
	//
	compileShader := func(typ gl.GLenum, src string) (gl.Shader, error) {
		sh := gl.CreateShader(typ)
		sh.Source(src)
		sh.Compile()
		if sh.Get(gl.COMPILE_STATUS) == gl.FALSE {
			sh.Delete()
			return 0, fmt.Errorf(sh.GetInfoLog())
		}
		return sh, nil
	}
	vx, err := compileShader(gl.VERTEX_SHADER, vertexShaderSource)
	if (err != nil) { return err }
	fg, err := compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource)
	if (err != nil) { return err }
	disp.shader = gl.CreateProgram()
	disp.shader.AttachShader(vx)
	disp.shader.AttachShader(fg)
	disp.shader.Link()
	disp.shader.DetachShader(vx)
	disp.shader.DetachShader(fg)
	vx.Delete()
	fg.Delete()
	if disp.shader.Get(gl.LINK_STATUS) == gl.FALSE {
		disp.shader.Delete()
		return fmt.Errorf(disp.shader.GetInfoLog())
	}
	disp.shader.Use()

	//
	// Generate and initialize the voxel model
	//
	bufferData := getVoxelBuffer(UI_DETAIL)
	disp.voxelBuffer = gl.GenBuffer()
	disp.voxelBuffer.Bind(gl.ARRAY_BUFFER)
	disp.voxelLen = len(bufferData)*3
	gl.BufferData(gl.ARRAY_BUFFER, disp.voxelLen*4, bufferData, gl.STATIC_DRAW)
	attribVert := disp.shader.GetAttribLocation("voxel")
	attribVert.EnableArray()
	attribVert.AttribPointer(3, gl.FLOAT, false, 3*4, uintptr(0))

	//
	// Initialize the shader by setting some constant uniforms
	//
	disp.shader.GetUniformLocation("light_color").Uniform3f(0.15, 0.15, 0.15)
	disp.shader.GetUniformLocation("light_vec").Uniform3f(mathgl.Vec3{1, 1, 1}.Normalize().Elem())
	disp.shader.GetUniformLocation("voxel_radius").Uniform1f(1)

	return nil
}

func (disp *Display) NumVoxels() int {
	return disp.cubeHeight*disp.cubeLength*disp.cubeWidth
}

func (disp *Display) SwapBuffers() {
	disp.shouldSwapBuffers = true
}

func (disp *Display) ResetView() {
	disp.camRot  = mathgl.QuatIdent()
	disp.camZoom = -160
}

func getVoxelBuffer(detail int) []mathgl.Vec3 {
	ico := float32(1 + math.Sqrt(5)) / 2
	verts := []mathgl.Vec3{
		{-1.0,  ico,  0.0},
		{ 1.0,  ico,  0.0},
		{-1.0, -ico,  0.0},
		{ 1.0, -ico,  0.0},
		{ 0.0, -1.0,  ico},
		{ 0.0,  1.0,  ico},
		{ 0.0, -1.0, -ico},
		{ 0.0,  1.0, -ico},
		{ ico,  0.0, -1.0},
		{ ico,  0.0,  1.0},
		{-ico,  0.0, -1.0},
		{-ico,  0.0,  1.0},
	}
	polys := []int{
		0,  11, 5,
		0,  5,  1,
		0,  1,  7,
		0,  7,  10,
		0,  10, 11,
		1,  5,  9,
		5,  11, 4,
		11, 10, 2,
		10, 7,  6,
		7,  1,  8,
		3,  9,  4,
		3,  4,  2,
		3,  2,  6,
		3,  6,  8,
		3,  8,  9,
		4,  9,  5,
		2,  4,  11,
		6,  2,  10,
		8,  6,  7,
		9,  8,  1,
	}
	bufferData := make([]mathgl.Vec3, len(polys))[:0]
	for _, p := range polys {
		v := verts[p].Normalize()
		bufferData = append(bufferData, v)
	}
	var tessellate func(data []mathgl.Vec3, level int) []mathgl.Vec3
	tessellate = func(data []mathgl.Vec3, level int) []mathgl.Vec3 {
		if (level == 0) {
			return data
		}
		newData := make([]mathgl.Vec3, len(data)*3)[:0]
		for i := 0; i < len(data); i += 3 {
			old := data[i:i+3]
			new := [3]mathgl.Vec3{}
			for j := range old {
				a := data[i+j]
				b := data[i+(j+1)%3]
				new[j] = mathgl.Vec3{
					a.X() - (a.X()-b.X())/2,
					a.Y() - (a.Y()-b.Y())/2,
					a.Z() - (a.Z()-b.Z())/2,
				}.Normalize()
			}
			newData = append(newData, old[0], new[0], new[2])
			newData = append(newData, old[1], new[1], new[0])
			newData = append(newData, old[2], new[2], new[1])
			newData = append(newData, new[0], new[1], new[2])
		}
		return tessellate(newData, level-1)
	}
	return tessellate(bufferData, detail)
}

const vertexShaderSource = `
	#version 120

	uniform float voxel_radius;
	uniform mat4 mat_mvp;

	attribute vec3 voxel;
	varying vec3 frag_normal;

	void main() {
		frag_normal = voxel;
		gl_Position = mat_mvp * vec4(voxel*voxel_radius, 1.0);
	}
`

const fragmentShaderSource = `
	#version 120

	uniform vec3 light_color;
	uniform vec3 light_vec;
	uniform vec3 color_led;

	varying vec3 frag_normal;

	void main() {
		float cosTheta = clamp(dot(frag_normal, light_vec), 0, 1);
		gl_FragColor = vec4(color_led + light_color * cosTheta, 1.0);
	}
`
<commit_msg>Downgrade required GLEW version<commit_after>/*
 * Copyright (c) 2014 PolyFloyd
 */

package main

import (
	"fmt"
	"math"
	"runtime"
	gl     "github.com/go-gl/gl"
	glfw   "github.com/go-gl/glfw3"
	mathgl "github.com/go-gl/mathgl/mgl32"
)

type Display struct {
	Buffer  []float32
	HideOff bool

	cubeHeight  int
	cubeLength  int
	cubeWidth   int

	camRot  mathgl.Quat
	camZoom float32

	frontBuffer       []float32
	shader            gl.Program
	shouldSwapBuffers bool
	voxelBuffer       gl.Buffer
	voxelLen          int
	win               *glfw.Window
}

func NewDisplay(w, h, l int) *Display {
	disp := &Display{
		cubeHeight:  h,
		cubeLength:  l,
		cubeWidth:   w,
		Buffer:      make([]float32, w*h*l * 3),
		frontBuffer: make([]float32, w*h*l * 3),
	}
	disp.ResetView()
	return disp
}

func (disp *Display) Start() {
	runtime.LockOSThread()
	runtime.GOMAXPROCS(runtime.NumCPU())

	for i := 0; i < len(disp.frontBuffer); i += 3 {
		disp.frontBuffer[i + 0] = 0.0
		disp.frontBuffer[i + 1] = 0.4
		disp.frontBuffer[i + 2] = 1.0
	}

	if err := disp.init(); err != nil {
		panic(err)
	}

	for !disp.win.ShouldClose() {
		if (disp.shouldSwapBuffers) {
			disp.frontBuffer, disp.Buffer = disp.Buffer, disp.frontBuffer
			disp.shouldSwapBuffers = false
		}
		gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

		disp.render()

		disp.win.SwapBuffers()
		glfw.PollEvents()
	}
	disp.win.Destroy()
	runtime.UnlockOSThread()
}

func (disp *Display) render() {
	uniformColor := disp.shader.GetUniformLocation("color_led")
	uniformMVP   := disp.shader.GetUniformLocation("mat_mvp")

	projection := mathgl.Perspective(
		UI_FOVY,
		func(w, h int) float32 {
			return float32(w) / float32(h)
		}(disp.win.GetSize()),
		UI_ZNEAR,
		UI_ZFAR,
	)
	center := mathgl.Translate3D(
		-(UI_SPACING*float32(disp.cubeWidth)/2  - UI_SPACING/2),
		-(UI_SPACING*float32(disp.cubeHeight)/2 - UI_SPACING/2),
		-(UI_SPACING*float32(disp.cubeLength)/2 - UI_SPACING/2),
	)
	view := func() mathgl.Mat4 {
		m := mathgl.Ident4()
		m = m.Mul4(mathgl.Translate3D(0, 0, disp.camZoom))
		m = m.Mul4(disp.camRot.Mat4())
		return m
	}()

	for x := 0; x < disp.cubeWidth; x++ {
		for y := 0; y < disp.cubeHeight; y++ {
			for z := 0; z < disp.cubeLength; z++ {
				i := x*disp.cubeHeight*disp.cubeLength + y*disp.cubeLength + z

				r := disp.frontBuffer[i*3 + 0]
				g := disp.frontBuffer[i*3 + 1]
				b := disp.frontBuffer[i*3 + 2]
				if disp.HideOff && (r==0 && g==0 && b==0) {
					continue
				}

				model := mathgl.Translate3D(
					float32(x) * UI_SPACING,
					float32(y) * UI_SPACING,
					float32(z) * UI_SPACING,
				).Mul4(center);

				mvp := projection.Mul4(view).Mul4(model)
				uniformMVP.UniformMatrix4f(false, (*[16]float32)(&mvp))
				uniformColor.Uniform3f(r, g, b)
				gl.DrawArrays(gl.TRIANGLES, 0, disp.voxelLen);
			}
		}
	}
}

func (disp *Display) init() error {
	//
	// Initialize GLFW and create a window
	//
	if !glfw.Init() {
		panic("Can't init GLFW!")
	}
	var err error
	disp.win, err = glfw.CreateWindow(UI_WIN_W, UI_WIN_H, INFO, nil, nil)
	if err != nil {
		panic(err)
	}
	disp.win.MakeContextCurrent()
	resize := func(w, h int) { gl.Viewport(0, 0, w, h) }
	disp.win.SetSizeCallback(func(win *glfw.Window, w, h int) {
		resize(w, h)
	})
	resize(disp.win.GetSize())
	glfw.SwapInterval(1)

	//
	// Initialize user input
	//
	var dragButtonDown bool
	var mousePosLastX float64
	var mousePosLastY float64
	disp.win.SetCursorPositionCallback(func(_ *glfw.Window, x, y float64) {
		deltaX := x - mousePosLastX
		deltaY := y - mousePosLastY
		mousePosLastX = x
		mousePosLastY = y
		if (dragButtonDown) {
			disp.camRot = mathgl.QuatRotate(float32(deltaX) / UI_DRAGDIV, mathgl.Vec3{0, 1, 0}).Mul(disp.camRot)
			disp.camRot = mathgl.QuatRotate(float32(deltaY) / UI_DRAGDIV, mathgl.Vec3{1, 0, 0}).Mul(disp.camRot)
		}
	})
	disp.win.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton,
		action glfw.Action, mods glfw.ModifierKey) {
		dragButtonDown = action == glfw.Press && button == glfw.MouseButtonLeft
	})
	disp.win.SetScrollCallback(func(_ *glfw.Window, dx, dy float64) {
		disp.camZoom += float32(dy) * UI_ZOOMACCEL
	})
	disp.win.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, scancode int,
		action glfw.Action, mods glfw.ModifierKey) {
		if (action != glfw.Release) {
			switch(key) {
			case glfw.KeyS: disp.HideOff = !disp.HideOff
			case glfw.KeyR: disp.ResetView()
			}
		}
	})

	//
	// Initialize OpenGL
	//
	if gl.Init() != gl.FALSE {
		return fmt.Errorf("Could not initialize OpenGL")
	}
	gl.Enable(gl.DEPTH_TEST)
	gl.ClearColor(0.12, 0.12, 0.12, 1.0)

	//
	// Compile the voxel shader
	//
	compileShader := func(typ gl.GLenum, src string) (gl.Shader, error) {
		sh := gl.CreateShader(typ)
		sh.Source(src)
		sh.Compile()
		if sh.Get(gl.COMPILE_STATUS) == gl.FALSE {
			sh.Delete()
			return 0, fmt.Errorf(sh.GetInfoLog())
		}
		return sh, nil
	}
	vx, err := compileShader(gl.VERTEX_SHADER, vertexShaderSource)
	if (err != nil) { return err }
	fg, err := compileShader(gl.FRAGMENT_SHADER, fragmentShaderSource)
	if (err != nil) { return err }
	disp.shader = gl.CreateProgram()
	disp.shader.AttachShader(vx)
	disp.shader.AttachShader(fg)
	disp.shader.Link()
	disp.shader.DetachShader(vx)
	disp.shader.DetachShader(fg)
	vx.Delete()
	fg.Delete()
	if disp.shader.Get(gl.LINK_STATUS) == gl.FALSE {
		disp.shader.Delete()
		return fmt.Errorf(disp.shader.GetInfoLog())
	}
	disp.shader.Use()

	//
	// Generate and initialize the voxel model
	//
	bufferData := getVoxelBuffer(UI_DETAIL)
	disp.voxelBuffer = gl.GenBuffer()
	disp.voxelBuffer.Bind(gl.ARRAY_BUFFER)
	disp.voxelLen = len(bufferData)*3
	gl.BufferData(gl.ARRAY_BUFFER, disp.voxelLen*4, bufferData, gl.STATIC_DRAW)
	attribVert := disp.shader.GetAttribLocation("voxel")
	attribVert.EnableArray()
	attribVert.AttribPointer(3, gl.FLOAT, false, 3*4, uintptr(0))

	//
	// Initialize the shader by setting some constant uniforms
	//
	disp.shader.GetUniformLocation("light_color").Uniform3f(0.15, 0.15, 0.15)
	disp.shader.GetUniformLocation("light_vec").Uniform3f(mathgl.Vec3{1, 1, 1}.Normalize().Elem())
	disp.shader.GetUniformLocation("voxel_radius").Uniform1f(1)

	return nil
}

func (disp *Display) NumVoxels() int {
	return disp.cubeHeight*disp.cubeLength*disp.cubeWidth
}

func (disp *Display) SwapBuffers() {
	disp.shouldSwapBuffers = true
}

func (disp *Display) ResetView() {
	disp.camRot  = mathgl.QuatIdent()
	disp.camZoom = -160
}

func getVoxelBuffer(detail int) []mathgl.Vec3 {
	ico := float32(1 + math.Sqrt(5)) / 2
	verts := []mathgl.Vec3{
		{-1.0,  ico,  0.0},
		{ 1.0,  ico,  0.0},
		{-1.0, -ico,  0.0},
		{ 1.0, -ico,  0.0},
		{ 0.0, -1.0,  ico},
		{ 0.0,  1.0,  ico},
		{ 0.0, -1.0, -ico},
		{ 0.0,  1.0, -ico},
		{ ico,  0.0, -1.0},
		{ ico,  0.0,  1.0},
		{-ico,  0.0, -1.0},
		{-ico,  0.0,  1.0},
	}
	polys := []int{
		0,  11, 5,
		0,  5,  1,
		0,  1,  7,
		0,  7,  10,
		0,  10, 11,
		1,  5,  9,
		5,  11, 4,
		11, 10, 2,
		10, 7,  6,
		7,  1,  8,
		3,  9,  4,
		3,  4,  2,
		3,  2,  6,
		3,  6,  8,
		3,  8,  9,
		4,  9,  5,
		2,  4,  11,
		6,  2,  10,
		8,  6,  7,
		9,  8,  1,
	}
	bufferData := make([]mathgl.Vec3, len(polys))[:0]
	for _, p := range polys {
		v := verts[p].Normalize()
		bufferData = append(bufferData, v)
	}
	var tessellate func(data []mathgl.Vec3, level int) []mathgl.Vec3
	tessellate = func(data []mathgl.Vec3, level int) []mathgl.Vec3 {
		if (level == 0) {
			return data
		}
		newData := make([]mathgl.Vec3, len(data)*3)[:0]
		for i := 0; i < len(data); i += 3 {
			old := data[i:i+3]
			new := [3]mathgl.Vec3{}
			for j := range old {
				a := data[i+j]
				b := data[i+(j+1)%3]
				new[j] = mathgl.Vec3{
					a.X() - (a.X()-b.X())/2,
					a.Y() - (a.Y()-b.Y())/2,
					a.Z() - (a.Z()-b.Z())/2,
				}.Normalize()
			}
			newData = append(newData, old[0], new[0], new[2])
			newData = append(newData, old[1], new[1], new[0])
			newData = append(newData, old[2], new[2], new[1])
			newData = append(newData, new[0], new[1], new[2])
		}
		return tessellate(newData, level-1)
	}
	return tessellate(bufferData, detail)
}

const vertexShaderSource = `
	#version 120

	uniform float voxel_radius;
	uniform mat4 mat_mvp;

	attribute vec3 voxel;
	varying vec3 frag_normal;

	void main() {
		frag_normal = voxel;
		gl_Position = mat_mvp * vec4(voxel*voxel_radius, 1.0);
	}
`

const fragmentShaderSource = `
	#version 120

	uniform vec3 light_color;
	uniform vec3 light_vec;
	uniform vec3 color_led;

	varying vec3 frag_normal;

	void main() {
		float cosTheta = clamp(dot(frag_normal, light_vec), 0, 1);
		gl_FragColor = vec4(color_led + light_color * cosTheta, 1.0);
	}
`
<|endoftext|>


<commit_before>package main

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

	"github.com/golang/groupcache/lru"
	"github.com/zyedidia/tcell"
	"github.com/zyedidia/tcell/encoding"
)

var topRow = 0
var leftColumn = 0
var height = 0
var width = 0
var offset = 0
var s tcell.Screen

func createDisplay() {
	loadDark()
	os.Setenv("TERM", "xterm-truecolor")
	sc, e := tcell.NewScreen()
	s = sc
	if e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}
	encoding.Register()

	if e = s.Init(); e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}

	s.SetStyle(tcell.StyleDefault.
		Foreground(tcell.ColorWhite).
		Background(tcell.ColorDefault))
	s.Clear()
	s.Show()
	w, h := s.Size()
	width = w
	height = h
	backing = make([][]Backing, h-1, h-1)
	for i := range backing {
		backing[i] = make([]Backing, w, w)
	}
}

//Display renders the editor
func Display(buffer *Buffer) {
	createDisplay()
	quit := make(chan struct{})
	b := make(chan [][]Backing)
	evName := ""
	go func() {
		for {
			backing = <-b
			drawBack(backing)
		}
	}()
	go func() {
		for {
			offset = drawRuler(topRow, height-1, s)
			var w = width - offset + 1
			lines := buffer.GetLines(topRow, leftColumn, height-1, w)
			t := time.Now()

			drawBuffer(b, topRow, lines)
			time2 := time.Now().Sub(t).String()

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, "                                                                                ")
			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, buffer.filename)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), len(buffer.filename)+1, height-1, time2)

			var text = strconv.Itoa(GetCursor().loc.row+1) + ":" + strconv.Itoa(GetCursor().loc.column+1) +
				"/" + strconv.Itoa(buffer.Len()) + ":" + strconv.Itoa(len(buffer.GetLine(GetCursor().loc.row)))

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text), height-1, text)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text)-2-len(evName), height-1, evName)
			s.Show()
			ev := s.PollEvent()
			switch ev := ev.(type) {
			case *tcell.EventKey:
				switch ev.Key() {
				case tcell.KeyDown:
					c := GetCursor()
					c.MoveDown()
				case tcell.KeyUp:
					c := GetCursor()
					c.MoveUp()
				case tcell.KeyLeft:
					c := GetCursor()
					c.MoveLeft()
				case tcell.KeyRight:
					c := GetCursor()
					c.MoveRight()
				case tcell.KeyPgDn:
					c := GetCursor()
					c.MovePageDown()
				case tcell.KeyPgUp:
					c := GetCursor()
					c.MovePageUp()
				case tcell.KeyEscape:
					close(quit)
					return
				case tcell.KeyCtrlD:
					c := GetCursor()
					buffer.Insert(c.loc.row, 0, buffer.GetLine(c.loc.row))
					c.MoveDown()
				case tcell.KeyCtrlY:
					c := GetCursor()
					buffer.RemoveRow(c.loc.row)
				case tcell.KeyDelete:
					c := GetCursor()
					buffer.Delete(c.loc.row, c.loc.column)
				case tcell.KeyBackspace2:
					c := GetCursor()
					l := buffer.Backspace(c.loc.row, c.loc.column)
					if c.loc.column == 0 && c.loc.row > 0 {
						c.MoveUp()
						c.MoveToEndOfLine()
						for index := 0; index < l; index++ {
							c.MoveLeft()
						}
					} else {
						c.MoveLeft()
					}
				case tcell.KeyCtrlS:
					buffer.Save()
				case tcell.KeyTab:
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string('\t')))
					c.MoveRight()
				case tcell.KeyRune:
					evName = ev.Name()
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					c.MoveRight()
				case tcell.KeyEnter:
					c := GetCursor()
					//buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					buffer.InsertEnter(c.loc.row, c.loc.column)
					c.MoveDown()
					c.MoveStartOfLine()
				default:
					evName = ev.Name()
				}

			case *tcell.EventResize:
				s.Sync()
				w, h := s.Size()
				width = w
				height = h
				backing = make([][]Backing, h-1, h-1)
				for i := range backing {
					backing[i] = make([]Backing, w, w)
				}
			}
		}
	}()

	<-quit

	s.Fini()
}

// Backing backs the display so we don't overwrite runes unnessecary
type Backing struct {
	value rune
	style tcell.Style
}

var backing = [][]Backing{}
var cache = lru.New(1000)

func drawBuffer(b chan [][]Backing, topRow int, lines [][]rune) {
	var style = getThemeColor("editor")
	c := GetCursor()
	var syn = Syntax{}
	for _, s := range syntaxDef {
		for _, f := range s.FileTypes {
			if strings.HasSuffix(buffer.filename, f) {
				syn = s
				break
			}
		}
	}

	for i := range backing {
		if len(lines)-1 < i {
			continue
		}
		jj := 0
		tokens := []Token{}
		key := string(lines[i])
		if val, ok := cache.Get(key); !ok {
			val = highlightLine(lines[i], syn)
			cache.Add(key, val)
			tokens = val.([]Token)
		} else {
			tokens = val.([]Token)
		}
		for _, r := range lines[i] {
			found := false
			for _, token := range tokens {
				if jj >= token.Loc[0] && jj < token.Loc[1] {
					backing[i][jj].style = getThemeColor(token.Scope...)
					found = true
					break
				}
			}

			backing[i][jj].value = r
			if !found {
				backing[i][jj].style = style
			}
			jj++
		}
		for index := len(lines[i]); index < width; index++ {
			backing[i][index].value = ' '
			backing[i][index].style = style
		}
	}

	if len(backing) > c.loc.row-topRow && len(backing[c.loc.row-topRow]) > c.loc.column-leftColumn {
		for i := range backing[c.loc.row-topRow] {
			backing[c.loc.row-topRow][i].style = backing[c.loc.row-topRow][i].style.Background(tcell.GetColor("#282828"))
		}
		// No cursor on enter
		if c.loc.column != -1 {
			if backing[c.loc.row-topRow][c.loc.column-leftColumn].value == '\n' {
				backing[c.loc.row-topRow][c.loc.column-leftColumn+1].style = tcell.StyleDefault.Reverse(true)
			} else {
				backing[c.loc.row-topRow][c.loc.column-leftColumn].style = tcell.StyleDefault.Reverse(true)
			}
		}
	}
	//drawBack(backing)
	b <- backing
}

func drawBack(backing [][]Backing) {
	for ir, row := range backing {
		x := offset
		for _, column := range row {
			if column.value == '\t' {
				s.SetContent(x, ir, ' ', nil, column.style)
				s.SetContent(x+1, ir, ' ', nil, column.style)
				s.SetContent(x+2, ir, ' ', nil, column.style)
				s.SetContent(x+3, ir, ' ', nil, column.style)
				x += 4
				continue
			}
			if column.value == '\n' {
				continue
			}
			s.SetContent(x, ir, column.value, nil, column.style)
			x++
		}
	}
}

func drawRuler(topRow, h int, s tcell.Screen) int {
	length := strconv.Itoa(len(fmt.Sprintf("%d", topRow+h)))
	ret := len(fmt.Sprintf("%d", topRow+h))
	style := getThemeColor("editor.ruler")
	for index := 0; index < h; index++ {
		puts(s, style, 0, index, fmt.Sprintf("%"+length+"d ", topRow+index+1))
	}
	return ret + 1
}
<commit_msg>Async draw<commit_after>package main

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

	"github.com/golang/groupcache/lru"
	"github.com/zyedidia/tcell"
	"github.com/zyedidia/tcell/encoding"
)

var topRow = 0
var leftColumn = 0
var height = 0
var width = 0
var offset = 0
var s tcell.Screen

func createDisplay() {
	loadDark()
	os.Setenv("TERM", "xterm-truecolor")
	sc, e := tcell.NewScreen()
	s = sc
	if e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}
	encoding.Register()

	if e = s.Init(); e != nil {
		fmt.Fprintf(os.Stderr, "%v\n", e)
		os.Exit(1)
	}

	s.SetStyle(tcell.StyleDefault.
		Foreground(tcell.ColorWhite).
		Background(tcell.ColorDefault))
	s.Clear()
	s.Show()
	w, h := s.Size()
	width = w
	height = h
}

//Display renders the editor
func Display(buffer *Buffer) {
	createDisplay()
	quit := make(chan struct{})
	b := make(chan [][]Backing)
	evName := ""
	go func() {
		for {
			t := time.Now()
			offset = drawRuler(topRow, height-1, s)
			var w = width - offset + 1
			lines := buffer.GetLines(topRow, leftColumn, height-1, w)
			go drawBufferHighlight(b, topRow, lines)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, "                                                                                ")
			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), 0, height-1, buffer.filename)

			var text = strconv.Itoa(GetCursor().loc.row+1) + ":" + strconv.Itoa(GetCursor().loc.column+1) +
				"/" + strconv.Itoa(buffer.Len()) + ":" + strconv.Itoa(len(buffer.GetLine(GetCursor().loc.row)))

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text), height-1, text)

			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), width-len(text)-2-len(evName), height-1, evName)

			drawBack(<-b)
			time2 := time.Now().Sub(t).String()
			puts(s, tcell.StyleDefault.
				Foreground(tcell.ColorWhite).
				Background(tcell.ColorDefault), len(buffer.filename)+1, height-1, time2)

			s.Show()
			ev := s.PollEvent()
			switch ev := ev.(type) {
			case *tcell.EventKey:
				switch ev.Key() {
				case tcell.KeyDown:
					c := GetCursor()
					c.MoveDown()
				case tcell.KeyUp:
					c := GetCursor()
					c.MoveUp()
				case tcell.KeyLeft:
					c := GetCursor()
					c.MoveLeft()
				case tcell.KeyRight:
					c := GetCursor()
					c.MoveRight()
				case tcell.KeyPgDn:
					c := GetCursor()
					c.MovePageDown()
				case tcell.KeyPgUp:
					c := GetCursor()
					c.MovePageUp()
				case tcell.KeyEscape:
					close(quit)
					return
				case tcell.KeyCtrlD:
					c := GetCursor()
					buffer.Insert(c.loc.row, 0, buffer.GetLine(c.loc.row))
					c.MoveDown()
				case tcell.KeyCtrlY:
					c := GetCursor()
					buffer.RemoveRow(c.loc.row)
				case tcell.KeyDelete:
					c := GetCursor()
					buffer.Delete(c.loc.row, c.loc.column)
				case tcell.KeyBackspace2:
					c := GetCursor()
					l := buffer.Backspace(c.loc.row, c.loc.column)
					if c.loc.column == 0 && c.loc.row > 0 {
						c.MoveUp()
						c.MoveToEndOfLine()
						for index := 0; index < l; index++ {
							c.MoveLeft()
						}
					} else {
						c.MoveLeft()
					}
				case tcell.KeyCtrlS:
					buffer.Save()
				case tcell.KeyTab:
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string('\t')))
					c.MoveRight()
				case tcell.KeyRune:
					evName = ev.Name()
					c := GetCursor()
					buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					c.MoveRight()
				case tcell.KeyEnter:
					c := GetCursor()
					//buffer.Insert(c.loc.row, c.loc.column, []rune(string(ev.Rune())))
					buffer.InsertEnter(c.loc.row, c.loc.column)
					c.MoveDown()
					c.MoveStartOfLine()
				default:
					evName = ev.Name()
				}

			case *tcell.EventResize:
				s.Sync()
			}
		}
	}()

	<-quit

	s.Fini()
}

// Backing backs the display so we don't overwrite runes unnessecary
type Backing struct {
	value rune
	style tcell.Style
}

var cache = lru.New(1000)

func drawBufferHighlight(b chan [][]Backing, topRow int, lines [][]rune) {
	backing := highlightLines(lines)
	backing = paintCursor(backing, topRow)
	b <- backing
}
func drawBuffer(b chan [][]Backing, topRow int, lines [][]rune) {
	backing := drawLines(lines)
	backing = paintCursor(backing, topRow)
	b <- backing
}
func paintCursor(backing [][]Backing, topRow int) [][]Backing {
	c := GetCursor()
	if len(backing) > c.loc.row-topRow && len(backing[c.loc.row-topRow]) > c.loc.column-leftColumn {
		for i := range backing[c.loc.row-topRow] {
			backing[c.loc.row-topRow][i].style = backing[c.loc.row-topRow][i].style.Background(tcell.GetColor("#282828"))
		}
		// No cursor on enter
		if c.loc.column != -1 {
			if backing[c.loc.row-topRow][c.loc.column-leftColumn].value == '\n' {
				backing[c.loc.row-topRow][c.loc.column-leftColumn+1].style = tcell.StyleDefault.Reverse(true)
			} else {
				backing[c.loc.row-topRow][c.loc.column-leftColumn].style = tcell.StyleDefault.Reverse(true)
			}
		}
	}
	return backing
}
func highlightLines(lines [][]rune) [][]Backing {
	w, h := s.Size()
	backing := make([][]Backing, h-1, h-1)
	for i := range backing {
		backing[i] = make([]Backing, w, w)
	}

	var style = getThemeColor("editor")
	var syn = Syntax{}
	for _, s := range syntaxDef {
		for _, f := range s.FileTypes {
			if strings.HasSuffix(buffer.filename, f) {
				syn = s
				break
			}
		}
	}
	for i := range backing {
		if len(lines)-1 < i {
			continue
		}
		jj := 0
		tokens := []Token{}
		key := string(lines[i])
		if val, ok := cache.Get(key); !ok {
			val = highlightLine(lines[i], syn)
			cache.Add(key, val)
			tokens = val.([]Token)
		} else {
			tokens = val.([]Token)
		}
		for _, r := range lines[i] {
			found := false
			for _, token := range tokens {
				if jj >= token.Loc[0] && jj < token.Loc[1] {
					backing[i][jj].style = getThemeColor(token.Scope...)
					found = true
					break
				}
			}

			backing[i][jj].value = r
			if !found {
				backing[i][jj].style = style
			}
			jj++
		}
		for index := len(lines[i]); index < width; index++ {
			backing[i][index].value = ' '
			backing[i][index].style = style
		}
	}
	return backing
}
func drawLines(lines [][]rune) [][]Backing {
	w, h := s.Size()
	backing := make([][]Backing, h-1, h-1)
	for i := range backing {
		backing[i] = make([]Backing, w, w)
	}

	var style = getThemeColor("editor")

	for i := range backing {
		if len(lines)-1 < i {
			continue
		}
		jj := 0
		for _, r := range lines[i] {
			backing[i][jj].value = r
			backing[i][jj].style = style
			jj++
		}
		for index := len(lines[i]); index < width; index++ {
			backing[i][index].value = ' '
			backing[i][index].style = style
		}
	}
	return backing
}
func drawBack(backing [][]Backing) {
	for ir, row := range backing {
		x := offset
		for _, column := range row {
			if column.value == '\t' {
				s.SetContent(x, ir, ' ', nil, column.style)
				s.SetContent(x+1, ir, ' ', nil, column.style)
				s.SetContent(x+2, ir, ' ', nil, column.style)
				s.SetContent(x+3, ir, ' ', nil, column.style)
				x += 4
				continue
			}
			if column.value == '\n' {
				continue
			}
			s.SetContent(x, ir, column.value, nil, column.style)
			x++
		}
	}
}

func drawRuler(topRow, h int, s tcell.Screen) int {
	length := strconv.Itoa(len(fmt.Sprintf("%d", topRow+h)))
	ret := len(fmt.Sprintf("%d", topRow+h))
	style := getThemeColor("editor.ruler")
	for index := 0; index < h; index++ {
		puts(s, style, 0, index, fmt.Sprintf("%"+length+"d ", topRow+index+1))
	}
	return ret + 1
}
<|endoftext|>


<commit_before>package logger

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

const (
	LOG_LEVEL_OFF = iota
	LOG_LEVEL_FATAL
	LOG_LEVEL_ERROR
	LOG_LEVEL_WARN
	LOG_LEVEL_INFO
	LOG_LEVEL_DEBUG
	LOG_LEVEL_TRACE
	LOG_LEVEL_ALL
)

var DefaultLevel int = LOG_LEVEL_INFO

type Logger struct {
	level  int
	prefix string
}

func NewLogger(level int, prefix string) *Logger {
	return &Logger{
		level:  level,
		prefix: prefix,
	}
}

func NewDefaultLogger(prefix string) *Logger {
	return &Logger{
		level:  DefaultLevel,
		prefix: prefix,
	}
}

func (l *Logger) Fatal(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_FATAL {
		l.Log("[fatal]", format, v...)
	}
	os.Exit(1)
}

func (l *Logger) Error(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_ERROR {
		l.Log("[error]", format, v...)
	}
}

func (l *Logger) Warn(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_WARN {
		l.Log("[warn] ", format, v...)
	}
}

func (l *Logger) Info(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_INFO {
		l.Log("[info] ", format, v...)
	}
}

func (l *Logger) Debug(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_DEBUG {
		l.Log("[debug]", format, v...)
	}
}

func (l *Logger) Trace(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_TRACE {
		l.Log("[trace]", format, v...)
	}
}

func (l *Logger) Log(level string, format string, v ...interface{}) {
	tim := time.Now().Format("2006-01-02 15:04:05.000000")
	out := fmt.Sprintf(fmt.Sprintf("%s\n", format), v...)

	if l.prefix != "" {
		fmt.Printf("[%s] %s (%s) %s", tim, level, l.prefix, out)
	} else {
		fmt.Printf("[%s] %s %s", tim, level, out)
	}
}
<commit_msg>Make it easier to set level<commit_after>package logger

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

const (
	LOG_LEVEL_OFF = iota
	LOG_LEVEL_FATAL
	LOG_LEVEL_ERROR
	LOG_LEVEL_WARN
	LOG_LEVEL_INFO
	LOG_LEVEL_DEBUG
	LOG_LEVEL_TRACE
	LOG_LEVEL_ALL
)

var DefaultLevel int = LOG_LEVEL_INFO

type Logger struct {
	level  int
	prefix string
}

func init() {
	if os.Getenv("DEBUG") != "" {
		DefaultLevel = LOG_LEVEL_DEBUG
	}

	switch os.Getenv("LOG_LEVEL") {
	case "OFF":
		DefaultLevel = LOG_LEVEL_OFF
	case "FATAL":
		DefaultLevel = LOG_LEVEL_FATAL
	case "ERROR":
		DefaultLevel = LOG_LEVEL_ERROR
	case "WARN":
		DefaultLevel = LOG_LEVEL_WARN
	case "INFO":
		DefaultLevel = LOG_LEVEL_INFO
	case "DEBUG":
		DefaultLevel = LOG_LEVEL_DEBUG
	case "TRACE":
		DefaultLevel = LOG_LEVEL_TRACE
	case "ALL":
		DefaultLevel = LOG_LEVEL_ALL
	}
}

func NewLogger(level int, prefix string) *Logger {
	return &Logger{
		level:  level,
		prefix: prefix,
	}
}

func NewDefaultLogger(prefix string) *Logger {
	return &Logger{
		level:  DefaultLevel,
		prefix: prefix,
	}
}

func (l *Logger) SetLevel(level int) {
	if level >= LOG_LEVEL_OFF && level <= LOG_LEVEL_ALL {
		l.level = level
	}
}

func (l *Logger) Fatal(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_FATAL {
		l.Log("[fatal]", format, v...)
	}
	os.Exit(1)
}

func (l *Logger) Error(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_ERROR {
		l.Log("[error]", format, v...)
	}
}

func (l *Logger) Warn(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_WARN {
		l.Log("[warn] ", format, v...)
	}
}

func (l *Logger) Info(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_INFO {
		l.Log("[info] ", format, v...)
	}
}

func (l *Logger) Debug(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_DEBUG {
		l.Log("[debug]", format, v...)
	}
}

func (l *Logger) Trace(format string, v ...interface{}) {
	if l.level >= LOG_LEVEL_TRACE {
		l.Log("[trace]", format, v...)
	}
}

func (l *Logger) Log(level string, format string, v ...interface{}) {
	tim := time.Now().Format("2006-01-02 15:04:05.000000")
	out := fmt.Sprintf(fmt.Sprintf("%s\n", format), v...)

	if l.prefix != "" {
		fmt.Printf("[%s] %s (%s) %s", tim, level, l.prefix, out)
	} else {
		fmt.Printf("[%s] %s %s", tim, level, out)
	}
}
<|endoftext|>


<commit_before>package golog

import (
	"flag"
)

// Prints everything this level and above. (Set to SILENT to disable).
var flag_minloglevel = flag.Int("vlog.minloglevel", INFO,
	"Log messages at or above this level. The "+
		"numbers of severity levels INFO, WARNING, "+
		"ERROR, and FATAL are 0, 1, 2, and 3, respectively")

// Logger.Log uses the level to determine whether or not to output the
// arguments. Logger.Log will output the provided arguments exactly, without
// additional formatting such as adding a prefix etc. In addition, Logger.Log
// must be thread safe. The FailNow() function flushes the Logger and performs
// some action. The action performed by FailNow() is deliberately unspecified,
// but could include os.Exit(1) or testing.(*T).FailNow(), etc.
type Logger interface {
	// If the message is to be logged, evaluates the closure and outputs
	// the result.
	Log(level int, closure func() *LogMessage)
	FailNow()
}

var DefaultLogger Logger = &loggerImpl{
	defaultLogOuters,
	flag_minloglevel,
	exitNow,
}

func NewLogger(outer LogOuter, minloglevel int, failFunc func()) Logger {
	return &loggerImpl{outer, &minloglevel, failFunc}
}

func NewDefaultLogger() Logger {
	return DefaultLogger
}

type loggerImpl struct {
	LogOuter
	// This is a reference type so we can add log writers before having
	// parsed flag_minloglevel.
	minloglevel *int
	failFunc    func()
}

func (l *loggerImpl) Log(level int, closure func() *LogMessage) {
	if level >= *l.minloglevel {
		l.Output(closure())
	}
}

func (l *loggerImpl) FailNow() {
	// TODO Flush log outer?
	l.failFunc()
}
<commit_msg>NewDefaultLogger clones MultiLogOuter<commit_after>package golog

import (
	"flag"
)

// Prints everything this level and above. (Set to SILENT to disable).
var flag_minloglevel = flag.Int("vlog.minloglevel", INFO,
	"Log messages at or above this level. The "+
		"numbers of severity levels INFO, WARNING, "+
		"ERROR, and FATAL are 0, 1, 2, and 3, respectively")

// Logger.Log uses the level to determine whether or not to output the
// arguments. Logger.Log will output the provided arguments exactly, without
// additional formatting such as adding a prefix etc. In addition, Logger.Log
// must be thread safe. The FailNow() function flushes the Logger and performs
// some action. The action performed by FailNow() is deliberately unspecified,
// but could include os.Exit(1) or testing.(*T).FailNow(), etc.
type Logger interface {
	// If the message is to be logged, evaluates the closure and outputs
	// the result.
	Log(level int, closure func() *LogMessage)
	FailNow()
}

var DefaultLogger Logger = &loggerImpl{
	defaultLogOuters,
	flag_minloglevel,
	exitNow,
}

func NewLogger(outer LogOuter, minloglevel int, failFunc func()) Logger {
	return &loggerImpl{outer, &minloglevel, failFunc}
}

func NewDefaultLogger() Logger {
	return &loggerImpl{
		NewDefaultMultiLogOuter(),
		flag_minloglevel,
		exitNow,
	}
}

type loggerImpl struct {
	LogOuter
	// This is a reference type so we can add log writers before having
	// parsed flag_minloglevel.
	minloglevel *int
	failFunc    func()
}

func (l *loggerImpl) Log(level int, closure func() *LogMessage) {
	if level >= *l.minloglevel {
		l.Output(closure())
	}
}

func (l *loggerImpl) FailNow() {
	// TODO Flush log outer?
	l.failFunc()
}
<|endoftext|>


<commit_before>package logrus

import (
	"io"
	"os"
	"sync"
)

type Logger struct {
	// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
	// file, or leave it default which is `os.Stdout`. You can also set this to
	// something more adventorous, such as logging to Kafka.
	Out io.Writer
	// Hooks for the logger instance. These allow firing events based on logging
	// levels and log entries. For example, to send errors to an error tracking
	// service, log to StatsD or dump the core on fatal errors.
	Hooks levelHooks
	// All log entries pass through the formatter before logged to Out. The
	// included formatters are `TextFormatter` and `JSONFormatter` for which
	// TextFormatter is the default. In development (when a TTY is attached) it
	// logs with colors, but to a file it wouldn't. You can easily implement your
	// own that implements the `Formatter` interface, see the `README` or included
	// formatters for examples.
	Formatter Formatter
	// The logging level the logger should log at. This is typically (and defaults
	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
	// logged. `logrus.Debug` is useful in
	Level Level
	// Used to sync writing to the log.
	mu sync.Mutex
}

// Creates a new logger. Configuration should be set by changing `Formatter`,
// `Out` and `Hooks` directly on the default logger instance. You can also just
// instantiate your own:
//
//    var log = &Logger{
//      Out: os.Stderr,
//      Formatter: new(JSONFormatter),
//      Hooks: make(levelHooks),
//      Level: logrus.DebugLevel,
//    }
//
// It's recommended to make this a global instance called `log`.
func New() *Logger {
	return &Logger{
		Out:       os.Stdout,
		Formatter: new(TextFormatter),
		Hooks:     make(levelHooks),
		Level:     InfoLevel,
	}
}

// Adds a field to the log entry, note that you it doesn't log until you call
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
// Ff you want multiple fields, use `WithFields`.
func (logger *Logger) WithField(key string, value interface{}) *Entry {
	return NewEntry(logger).WithField(key, value)
}

// Adds a struct of fields to the log entry. All it does is call `WithField` for
// each `Field`.
func (logger *Logger) WithFields(fields Fields) *Entry {
	return NewEntry(logger).WithFields(fields)
}

func (logger *Logger) Debugf(format string, args ...interface{}) {
	NewEntry(logger).Debugf(format, args...)
}

func (logger *Logger) Infof(format string, args ...interface{}) {
	NewEntry(logger).Infof(format, args...)
}

func (logger *Logger) Printf(format string, args ...interface{}) {
	NewEntry(logger).Printf(format, args...)
}

func (logger *Logger) Warnf(format string, args ...interface{}) {
	NewEntry(logger).Warnf(format, args...)
}

func (logger *Logger) Warningf(format string, args ...interface{}) {
	NewEntry(logger).Warnf(format, args...)
}

func (logger *Logger) Errorf(format string, args ...interface{}) {
	NewEntry(logger).Errorf(format, args...)
}

func (logger *Logger) Fatalf(format string, args ...interface{}) {
	NewEntry(logger).Fatalf(format, args...)
}

func (logger *Logger) Panicf(format string, args ...interface{}) {
	NewEntry(logger).Panicf(format, args...)
}

func (logger *Logger) Debug(args ...interface{}) {
	NewEntry(logger).Debug(args...)
}

func (logger *Logger) Info(args ...interface{}) {
	NewEntry(logger).Info(args...)
}

func (logger *Logger) Print(args ...interface{}) {
	NewEntry(logger).Info(args...)
}

func (logger *Logger) Warn(args ...interface{}) {
	NewEntry(logger).Warn(args...)
}

func (logger *Logger) Warning(args ...interface{}) {
	NewEntry(logger).Warn(args...)
}

func (logger *Logger) Error(args ...interface{}) {
	NewEntry(logger).Error(args...)
}

func (logger *Logger) Fatal(args ...interface{}) {
	NewEntry(logger).Fatal(args...)
}

func (logger *Logger) Panic(args ...interface{}) {
	NewEntry(logger).Panic(args...)
}

func (logger *Logger) Debugln(args ...interface{}) {
	NewEntry(logger).Debugln(args...)
}

func (logger *Logger) Infoln(args ...interface{}) {
	NewEntry(logger).Infoln(args...)
}

func (logger *Logger) Println(args ...interface{}) {
	NewEntry(logger).Println(args...)
}

func (logger *Logger) Warnln(args ...interface{}) {
	NewEntry(logger).Warnln(args...)
}

func (logger *Logger) Warningln(args ...interface{}) {
	NewEntry(logger).Warnln(args...)
}

func (logger *Logger) Errorln(args ...interface{}) {
	NewEntry(logger).Errorln(args...)
}

func (logger *Logger) Fatalln(args ...interface{}) {
	NewEntry(logger).Fatalln(args...)
}

func (logger *Logger) Panicln(args ...interface{}) {
	NewEntry(logger).Panicln(args...)
}
<commit_msg>improved: move level check from entry to logger and bail out faster<commit_after>package logrus

import (
	"io"
	"os"
	"sync"
)

type Logger struct {
	// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
	// file, or leave it default which is `os.Stdout`. You can also set this to
	// something more adventorous, such as logging to Kafka.
	Out io.Writer
	// Hooks for the logger instance. These allow firing events based on logging
	// levels and log entries. For example, to send errors to an error tracking
	// service, log to StatsD or dump the core on fatal errors.
	Hooks levelHooks
	// All log entries pass through the formatter before logged to Out. The
	// included formatters are `TextFormatter` and `JSONFormatter` for which
	// TextFormatter is the default. In development (when a TTY is attached) it
	// logs with colors, but to a file it wouldn't. You can easily implement your
	// own that implements the `Formatter` interface, see the `README` or included
	// formatters for examples.
	Formatter Formatter
	// The logging level the logger should log at. This is typically (and defaults
	// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
	// logged. `logrus.Debug` is useful in
	Level Level
	// Used to sync writing to the log.
	mu sync.Mutex
}

// Creates a new logger. Configuration should be set by changing `Formatter`,
// `Out` and `Hooks` directly on the default logger instance. You can also just
// instantiate your own:
//
//    var log = &Logger{
//      Out: os.Stderr,
//      Formatter: new(JSONFormatter),
//      Hooks: make(levelHooks),
//      Level: logrus.DebugLevel,
//    }
//
// It's recommended to make this a global instance called `log`.
func New() *Logger {
	return &Logger{
		Out:       os.Stdout,
		Formatter: new(TextFormatter),
		Hooks:     make(levelHooks),
		Level:     InfoLevel,
	}
}

// Adds a field to the log entry, note that you it doesn't log until you call
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
// Ff you want multiple fields, use `WithFields`.
func (logger *Logger) WithField(key string, value interface{}) *Entry {
	return NewEntry(logger).WithField(key, value)
}

// Adds a struct of fields to the log entry. All it does is call `WithField` for
// each `Field`.
func (logger *Logger) WithFields(fields Fields) *Entry {
	return NewEntry(logger).WithFields(fields)
}

func (logger *Logger) Debugf(format string, args ...interface{}) {
	if logger.Level >= DebugLevel {
		NewEntry(logger).Debugf(format, args...)
	}
}

func (logger *Logger) Infof(format string, args ...interface{}) {
	if logger.Level >= InfoLevel {
		NewEntry(logger).Infof(format, args...)
	}
}

func (logger *Logger) Printf(format string, args ...interface{}) {
	NewEntry(logger).Printf(format, args...)
}

func (logger *Logger) Warnf(format string, args ...interface{}) {
	if logger.Level >= WarnLevel {
		NewEntry(logger).Warnf(format, args...)
	}
}

func (logger *Logger) Warningf(format string, args ...interface{}) {
	if logger.Level >= WarnLevel {
		NewEntry(logger).Warnf(format, args...)
	}
}

func (logger *Logger) Errorf(format string, args ...interface{}) {
	if logger.Level >= ErrorLevel {
		NewEntry(logger).Errorf(format, args...)
	}
}

func (logger *Logger) Fatalf(format string, args ...interface{}) {
	if logger.Level >= FatalLevel {
		NewEntry(logger).Fatalf(format, args...)
	}
}

func (logger *Logger) Panicf(format string, args ...interface{}) {
	if logger.Level >= PanicLevel {
		NewEntry(logger).Panicf(format, args...)
	}
}

func (logger *Logger) Debug(args ...interface{}) {
	if logger.Level >= DebugLevel {
		NewEntry(logger).Debug(args...)
	}
}

func (logger *Logger) Info(args ...interface{}) {
	if logger.Level >= InfoLevel {
		NewEntry(logger).Info(args...)
	}
}

func (logger *Logger) Print(args ...interface{}) {
	NewEntry(logger).Info(args...)
}

func (logger *Logger) Warn(args ...interface{}) {
	if logger.Level >= WarnLevel {
		NewEntry(logger).Warn(args...)
	}
}

func (logger *Logger) Warning(args ...interface{}) {
	if logger.Level >= WarnLevel {
		NewEntry(logger).Warn(args...)
	}
}

func (logger *Logger) Error(args ...interface{}) {
	if logger.Level >= ErrorLevel {
		NewEntry(logger).Error(args...)
	}
}

func (logger *Logger) Fatal(args ...interface{}) {
	if logger.Level >= FatalLevel {
		NewEntry(logger).Fatal(args...)
	}
}

func (logger *Logger) Panic(args ...interface{}) {
	if logger.Level >= PanicLevel {
		NewEntry(logger).Panic(args...)
	}
}

func (logger *Logger) Debugln(args ...interface{}) {
	if logger.Level >= DebugLevel {
		NewEntry(logger).Debugln(args...)
	}
}

func (logger *Logger) Infoln(args ...interface{}) {
	if logger.Level >= InfoLevel {
		NewEntry(logger).Infoln(args...)
	}
}

func (logger *Logger) Println(args ...interface{}) {
	NewEntry(logger).Println(args...)
}

func (logger *Logger) Warnln(args ...interface{}) {
	if logger.Level >= WarnLevel {
		NewEntry(logger).Warnln(args...)
	}
}

func (logger *Logger) Warningln(args ...interface{}) {
	if logger.Level >= WarnLevel {
		NewEntry(logger).Warnln(args...)
	}
}

func (logger *Logger) Errorln(args ...interface{}) {
	if logger.Level >= ErrorLevel {
		NewEntry(logger).Errorln(args...)
	}
}

func (logger *Logger) Fatalln(args ...interface{}) {
	if logger.Level >= FatalLevel {
		NewEntry(logger).Fatalln(args...)
	}
}

func (logger *Logger) Panicln(args ...interface{}) {
	if logger.Level >= PanicLevel {
		NewEntry(logger).Panicln(args...)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zap

import (
	"fmt"
	"io"
	"os"
	"sync/atomic"
	"time"
)

// For stubbing in tests.
var _timeNow = time.Now
var _errSink io.Writer = os.Stderr

// A Logger enables leveled, structured logging. All methods are safe for
// concurrent use.
type Logger interface {
	// Check if output at a specific level is enabled.
	Enabled(Level) bool
	// Check the minimum enabled log level.
	Level() Level
	// Change the level of this logger, as well as all its ancestors and
	// descendants. This makes makes it easy to change the log level at runtime
	// without restarting your application.
	SetLevel(Level)
	// Create a child logger, and optionally add some context to that logger.
	With(...Field) Logger

	// Log a message at the given level. Messages include any context that's
	// accumulated on the logger, as well as any fields added at the log site.
	Debug(string, ...Field)
	Info(string, ...Field)
	Warn(string, ...Field)
	Error(string, ...Field)
	Panic(string, ...Field)
	Fatal(string, ...Field)
}

type jsonLogger struct {
	level *int32 // atomic
	enc   encoder
	errW  io.Writer
	w     io.Writer
}

// NewJSON returns a logger that formats its output as JSON. Zap uses a
// customized JSON encoder to avoid reflection and minimize allocations.
//
// Any passed fields are added to the logger as context.
func NewJSON(lvl Level, sink io.Writer, fields ...Field) Logger {
	integerLevel := int32(lvl)
	jl := &jsonLogger{
		level: &integerLevel,
		enc:   newJSONEncoder(),
		w:     sink,
	}
	if err := jl.enc.AddFields(fields); err != nil {
		jl.internalError(err.Error())
	}
	return jl
}

func (jl *jsonLogger) Level() Level {
	return Level(atomic.LoadInt32(jl.level))
}

func (jl *jsonLogger) SetLevel(lvl Level) {
	atomic.StoreInt32(jl.level, int32(lvl))
}

func (jl *jsonLogger) Enabled(lvl Level) bool {
	return lvl >= jl.Level()
}

func (jl *jsonLogger) With(fields ...Field) Logger {
	clone := &jsonLogger{
		level: jl.level,
		enc:   jl.enc.Clone(),
		w:     jl.w,
	}
	if err := clone.enc.AddFields(fields); err != nil {
		jl.internalError(err.Error())
	}
	return clone
}

func (jl *jsonLogger) Debug(msg string, fields ...Field) {
	jl.log(Debug, msg, fields)
}

func (jl *jsonLogger) Info(msg string, fields ...Field) {
	jl.log(Info, msg, fields)
}

func (jl *jsonLogger) Warn(msg string, fields ...Field) {
	jl.log(Warn, msg, fields)
}

func (jl *jsonLogger) Error(msg string, fields ...Field) {
	jl.log(Error, msg, fields)
}

func (jl *jsonLogger) Panic(msg string, fields ...Field) {
	jl.log(Panic, msg, fields)
	panic(msg)
}

func (jl *jsonLogger) Fatal(msg string, fields ...Field) {
	jl.log(Fatal, msg, fields)
	os.Exit(1)
}

func (jl *jsonLogger) log(lvl Level, msg string, fields []Field) {
	if !jl.Enabled(lvl) {
		return
	}

	temp := newJSONEncoder()
	temp.bytes = append(temp.bytes, jl.enc.(*jsonEncoder).bytes...)
	if err := temp.AddFields(fields); err != nil {
		jl.internalError(err.Error())
	}
	temp.WriteMessage(jl.w, lvl.String(), msg, _timeNow())
	temp.Free()
}

func (jl *jsonLogger) internalError(msg string) {
	fmt.Fprint(_errSink, msg)
}
<commit_msg>Use a var block in logger<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package zap

import (
	"fmt"
	"io"
	"os"
	"sync/atomic"
	"time"
)

// For stubbing in tests.
var (
	_timeNow           = time.Now
	_errSink io.Writer = os.Stderr
)

// A Logger enables leveled, structured logging. All methods are safe for
// concurrent use.
type Logger interface {
	// Check if output at a specific level is enabled.
	Enabled(Level) bool
	// Check the minimum enabled log level.
	Level() Level
	// Change the level of this logger, as well as all its ancestors and
	// descendants. This makes makes it easy to change the log level at runtime
	// without restarting your application.
	SetLevel(Level)
	// Create a child logger, and optionally add some context to that logger.
	With(...Field) Logger

	// Log a message at the given level. Messages include any context that's
	// accumulated on the logger, as well as any fields added at the log site.
	Debug(string, ...Field)
	Info(string, ...Field)
	Warn(string, ...Field)
	Error(string, ...Field)
	Panic(string, ...Field)
	Fatal(string, ...Field)
}

type jsonLogger struct {
	level *int32 // atomic
	enc   encoder
	errW  io.Writer
	w     io.Writer
}

// NewJSON returns a logger that formats its output as JSON. Zap uses a
// customized JSON encoder to avoid reflection and minimize allocations.
//
// Any passed fields are added to the logger as context.
func NewJSON(lvl Level, sink io.Writer, fields ...Field) Logger {
	integerLevel := int32(lvl)
	jl := &jsonLogger{
		level: &integerLevel,
		enc:   newJSONEncoder(),
		w:     sink,
	}
	if err := jl.enc.AddFields(fields); err != nil {
		jl.internalError(err.Error())
	}
	return jl
}

func (jl *jsonLogger) Level() Level {
	return Level(atomic.LoadInt32(jl.level))
}

func (jl *jsonLogger) SetLevel(lvl Level) {
	atomic.StoreInt32(jl.level, int32(lvl))
}

func (jl *jsonLogger) Enabled(lvl Level) bool {
	return lvl >= jl.Level()
}

func (jl *jsonLogger) With(fields ...Field) Logger {
	clone := &jsonLogger{
		level: jl.level,
		enc:   jl.enc.Clone(),
		w:     jl.w,
	}
	if err := clone.enc.AddFields(fields); err != nil {
		jl.internalError(err.Error())
	}
	return clone
}

func (jl *jsonLogger) Debug(msg string, fields ...Field) {
	jl.log(Debug, msg, fields)
}

func (jl *jsonLogger) Info(msg string, fields ...Field) {
	jl.log(Info, msg, fields)
}

func (jl *jsonLogger) Warn(msg string, fields ...Field) {
	jl.log(Warn, msg, fields)
}

func (jl *jsonLogger) Error(msg string, fields ...Field) {
	jl.log(Error, msg, fields)
}

func (jl *jsonLogger) Panic(msg string, fields ...Field) {
	jl.log(Panic, msg, fields)
	panic(msg)
}

func (jl *jsonLogger) Fatal(msg string, fields ...Field) {
	jl.log(Fatal, msg, fields)
	os.Exit(1)
}

func (jl *jsonLogger) log(lvl Level, msg string, fields []Field) {
	if !jl.Enabled(lvl) {
		return
	}

	temp := newJSONEncoder()
	temp.bytes = append(temp.bytes, jl.enc.(*jsonEncoder).bytes...)
	if err := temp.AddFields(fields); err != nil {
		jl.internalError(err.Error())
	}
	temp.WriteMessage(jl.w, lvl.String(), msg, _timeNow())
	temp.Free()
}

func (jl *jsonLogger) internalError(msg string) {
	fmt.Fprint(_errSink, msg)
}
<|endoftext|>


<commit_before>package log

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

	loggly "github.com/treetopllc/go-loggly"
)

type LogglyClient struct {
	Basic
	client loggly.Client
}

func NewLogglyClient(token string, tags ...string) *LogglyClient {
	c := &LogglyClient{
		client: loggly.NewClient(token, tags...),
	}
	c.Basic = NewBasic(c, "", 0)
	return c
}

func (lc *LogglyClient) Send(e *LogglyEntry) {
	if e != nil {
		b, err := json.Marshal(e) //no fields here can json err
		if err != nil {
			fmt.Println("Error sending to loggly: ", err)
		} else {
			lc.Write(b)
		}
	}
}

func (lc *LogglyClient) Write(msg []byte) (int, error) {
	go lc.client.Send(msg)
	return len(msg), nil
}

type LogglyEntry struct {
	Logs  StringLogger
	Level LogLevel

	Request struct {
		Proto  string
		Method string
		Host   string
		Path   string
		Query  string
		Body   string
		Header http.Header
		UserID string
	}
	Response struct {
		Body     interface{}
		Status   int
		Duration int64
	}
	startTime time.Time //Used for duration, not encoded
}

func NewLogglyEntry() *LogglyEntry {
	return &LogglyEntry{
		startTime: time.Now(),
		Level:     INFO,
	}
}

func (le *LogglyEntry) SetRequest(req *http.Request) {
	le.Request.Method = req.Method
	le.Request.Proto = req.Proto
	le.Request.Host = req.URL.Host
	le.Request.Path = req.URL.Path
	le.Request.Query = req.URL.RawQuery
	le.Request.Header = req.Header
	if req.Body != nil {
		le.Request.Body = requestBody(req)
	}
}
func (le *LogglyEntry) SetUserID(id string) {
	le.Request.UserID = id
}
func (le *LogglyEntry) SetResponse(status int, body interface{}) {
	if debug {
		le.Response.Body = responseBody(body)
	}
	le.Response.Status = status
	le.Response.Duration = time.Since(le.startTime).Nanoseconds() / 1000000 //1 ms = 1000000ns
}

func (le *LogglyEntry) Write(b []byte) (int, error) {
	le.Level.Set(INFO)
	le.Logs.Log(string(b))
	return len(b), nil
}
func (le *LogglyEntry) Log(msg ...interface{}) {
	le.Level.Set(INFO)
	le.Logs.Log(msg...)
}
func (le *LogglyEntry) Logf(msg string, args ...interface{}) {
	le.Level.Set(INFO)
	le.Logs.Logf(msg, args...)
}
func (le *LogglyEntry) Error(err error) {
	le.Level.Set(ERROR)
	le.Logs.Error(err)
}
func (le *LogglyEntry) Errorf(msg string, args ...interface{}) {
	le.Level.Set(ERROR)
	le.Logs.Errorf(msg, args...)
}
func (le *LogglyEntry) Panic(msg interface{}) {
	le.Level.Set(PANIC)
	le.Logs.Panic(msg)
}
func (le *LogglyEntry) Panicf(msg string, args ...interface{}) {
	le.Level.Set(PANIC)
	le.Logs.Panicf(msg, args...)
}
<commit_msg>Support logging product type to loggly<commit_after>package log

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

	loggly "github.com/treetopllc/go-loggly"
)

type LogglyClient struct {
	Basic
	client loggly.Client
}

func NewLogglyClient(token string, tags ...string) *LogglyClient {
	c := &LogglyClient{
		client: loggly.NewClient(token, tags...),
	}
	c.Basic = NewBasic(c, "", 0)
	return c
}

func (lc *LogglyClient) Send(e *LogglyEntry) {
	if e != nil {
		b, err := json.Marshal(e) //no fields here can json err
		if err != nil {
			fmt.Println("Error sending to loggly: ", err)
		} else {
			lc.Write(b)
		}
	}
}

func (lc *LogglyClient) Write(msg []byte) (int, error) {
	go lc.client.Send(msg)
	return len(msg), nil
}

type LogglyEntry struct {
	Logs  StringLogger
	Level LogLevel

	Request struct {
		Proto       string
		Method      string
		Host        string
		Path        string
		Query       string
		Body        string
		Header      http.Header
		UserID      string
		ProductType string
	}
	Response struct {
		Body     interface{}
		Status   int
		Duration int64
	}
	startTime time.Time //Used for duration, not encoded
}

func NewLogglyEntry() *LogglyEntry {
	return &LogglyEntry{
		startTime: time.Now(),
		Level:     INFO,
	}
}

func (le *LogglyEntry) SetRequest(req *http.Request) {
	le.Request.Method = req.Method
	le.Request.Proto = req.Proto
	le.Request.Host = req.URL.Host
	le.Request.Path = req.URL.Path
	le.Request.Query = req.URL.RawQuery
	le.Request.Header = req.Header
	if req.Body != nil {
		le.Request.Body = requestBody(req)
	}
}
func (le *LogglyEntry) SetUserID(id string) {
	le.Request.UserID = id
}
func (le *LogglyEntry) SetResponse(status int, body interface{}) {
	if debug {
		le.Response.Body = responseBody(body)
	}
	le.Response.Status = status
	le.Response.Duration = time.Since(le.startTime).Nanoseconds() / 1000000 //1 ms = 1000000ns
}

func (le *LogglyEntry) SetProductType(pt string) {
	le.Request.ProductType = pt
}

func (le *LogglyEntry) Write(b []byte) (int, error) {
	le.Level.Set(INFO)
	le.Logs.Log(string(b))
	return len(b), nil
}
func (le *LogglyEntry) Log(msg ...interface{}) {
	le.Level.Set(INFO)
	le.Logs.Log(msg...)
}
func (le *LogglyEntry) Logf(msg string, args ...interface{}) {
	le.Level.Set(INFO)
	le.Logs.Logf(msg, args...)
}
func (le *LogglyEntry) Error(err error) {
	le.Level.Set(ERROR)
	le.Logs.Error(err)
}
func (le *LogglyEntry) Errorf(msg string, args ...interface{}) {
	le.Level.Set(ERROR)
	le.Logs.Errorf(msg, args...)
}
func (le *LogglyEntry) Panic(msg interface{}) {
	le.Level.Set(PANIC)
	le.Logs.Panic(msg)
}
func (le *LogglyEntry) Panicf(msg string, args ...interface{}) {
	le.Level.Set(PANIC)
	le.Logs.Panicf(msg, args...)
}
<|endoftext|>


<commit_before>package sub

import (
	"bytes"
	"context"
	"fmt"
	"path/filepath"
	"strconv"
	"strings"
	"text/tabwriter"

	"github.com/igungor/cmd/filmdizibot/bot"
	"github.com/igungor/cmd/filmdizibot/command/sub/provider"
	"github.com/igungor/telegram"
)

type Sub struct {
	providers []provider.Provider
}

func New() *Sub {
	return &Sub{
		providers: []provider.Provider{
			provider.NewAddic7ed(),
		},
	}
}

func (s *Sub) Name() string { return "sub" }

func (s *Sub) Usage() string { return "sub <idx>\ndl <idx>" }

func (s *Sub) Match(msg string) bool { return strings.HasPrefix(msg, "sub") }

func (s *Sub) Run(ctx context.Context, b *bot.Bot, msg *bot.Message) {
	chat := msg.Chat.ID
	args := msg.Args()

	if len(args) == 0 {
		b.SendMessage(chat, "choose something")
		return
	}

	if args[0] == "dl" {
		err := s.handleDownload(ctx, b, msg, args[1:])
		if err != nil {
			b.SendMessage(chat, err.Error())
			return
		}
		b.SendMessage(chat, "subtitle is downloaded!")
		return
	}

	if len(args) > 1 {
		b.SendMessage(chat, "choose only one thing")
		return
	}

	subs, err := s.handleSelect(ctx, b, msg, args)
	if err != nil {
		b.SendMessage(chat, err.Error())
		return
	}

	if len(subs) == 0 {
		b.SendMessage(chat, "no subtitle found")
		return
	}

	sub := subs[0]
	title := fmt.Sprintf("%v S%2dE%2d", sub.Title, sub.Season, sub.Episode)
	var buf bytes.Buffer
	tw := tabwriter.NewWriter(&buf, 4, 4, 1, ' ', tabwriter.Debug)
	tw.Write([]byte(fmt.Sprintf("Subtitles for %q\n", title)))
	tw.Write([]byte("IDX | EP | Title | Lang | Release\n"))
	for i, sub := range subs {
		line := fmt.Sprintf("%02d %v\n", i+1, sub)
		tw.Write([]byte(line))
	}
	tw.Flush()

	md := telegram.WithParseMode(telegram.ModeMarkdown)
	b.SendMessage(chat, buf.String(), md)
}

func (s *Sub) handleSelect(ctx context.Context, b *bot.Bot, msg *bot.Message, args []string) ([]*provider.Subtitle, error) {
	sel := args[0]
	idx, err := strconv.ParseInt(sel, 10, 32)
	if err != nil {
		return nil, fmt.Errorf("wrong selection: %v", err)
	}

	children := b.Children()
	if len(children) < int(idx) {
		return nil, fmt.Errorf("wrong selection: no such idx")
	}

	f := children[idx-1]
	if f.IsDir() {
		return nil, fmt.Errorf("wrong selection: choose a file")
	}

	b.SelectFile(f)

	// FIXME: make it more generic
	subs, err := s.providers[0].Query(ctx, f.Name)
	if err != nil {
		return nil, fmt.Errorf("error listing subtitles: %v", err)
	}

	b.SetSubtitles(subs)
	return subs, nil
}

func (s *Sub) handleDownload(ctx context.Context, b *bot.Bot, msg *bot.Message, args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("choose something")
	}

	idx, err := strconv.ParseInt(args[0], 10, 32)
	if err != nil {
		return fmt.Errorf("wrong selection: %v", err)
	}

	subs := b.Subtitles()
	if len(subs) < int(idx) {
		return fmt.Errorf("wrong selection: no such idx")
	}

	sub := subs[idx-1]
	rc, err := s.providers[0].Download(ctx, sub)
	if err != nil {
		return fmt.Errorf("error downloading subtitle: %v", err)
	}
	defer rc.Close()

	f := b.SelectedFile()
	ext := filepath.Ext(f.Name)
	fname := strings.TrimSuffix(f.Name, ext)
	fname = fmt.Sprintf("%v.srt", fname)

	err = b.UploadFile(ctx, rc, fname, f.ParentID)
	if err != nil {
		return fmt.Errorf("error uploading subtitle: %v", err)
	}
	return nil
}
<commit_msg>sub: fix 2-digit numbers not shown properly<commit_after>package sub

import (
	"bytes"
	"context"
	"fmt"
	"path/filepath"
	"strconv"
	"strings"
	"text/tabwriter"

	"github.com/igungor/cmd/filmdizibot/bot"
	"github.com/igungor/cmd/filmdizibot/command/sub/provider"
	"github.com/igungor/telegram"
)

type Sub struct {
	providers []provider.Provider
}

func New() *Sub {
	return &Sub{
		providers: []provider.Provider{
			provider.NewAddic7ed(),
		},
	}
}

func (s *Sub) Name() string { return "sub" }

func (s *Sub) Usage() string { return "sub <idx>\ndl <idx>" }

func (s *Sub) Match(msg string) bool { return strings.HasPrefix(msg, "sub") }

func (s *Sub) Run(ctx context.Context, b *bot.Bot, msg *bot.Message) {
	chat := msg.Chat.ID
	args := msg.Args()

	if len(args) == 0 {
		b.SendMessage(chat, "choose something")
		return
	}

	if args[0] == "dl" {
		err := s.handleDownload(ctx, b, msg, args[1:])
		if err != nil {
			b.SendMessage(chat, err.Error())
			return
		}
		b.SendMessage(chat, "subtitle is downloaded!")
		return
	}

	if len(args) > 1 {
		b.SendMessage(chat, "choose only one thing")
		return
	}

	subs, err := s.handleSelect(ctx, b, msg, args)
	if err != nil {
		b.SendMessage(chat, err.Error())
		return
	}

	if len(subs) == 0 {
		b.SendMessage(chat, "no subtitle found")
		return
	}

	sub := subs[0]
	title := fmt.Sprintf("%v S%02dE%02d", sub.Title, sub.Season, sub.Episode)
	var buf bytes.Buffer
	tw := tabwriter.NewWriter(&buf, 4, 4, 1, ' ', tabwriter.Debug)
	tw.Write([]byte(fmt.Sprintf("Subtitles for %q\n", title)))
	tw.Write([]byte("IDX | EP | Title | Lang | Release\n"))
	for i, sub := range subs {
		line := fmt.Sprintf("%02d %v\n", i+1, sub)
		tw.Write([]byte(line))
	}
	tw.Flush()

	md := telegram.WithParseMode(telegram.ModeMarkdown)
	b.SendMessage(chat, buf.String(), md)
}

func (s *Sub) handleSelect(ctx context.Context, b *bot.Bot, msg *bot.Message, args []string) ([]*provider.Subtitle, error) {
	sel := args[0]
	idx, err := strconv.ParseInt(sel, 10, 32)
	if err != nil {
		return nil, fmt.Errorf("wrong selection: %v", err)
	}

	children := b.Children()
	if len(children) < int(idx) {
		return nil, fmt.Errorf("wrong selection: no such idx")
	}

	f := children[idx-1]
	if f.IsDir() {
		return nil, fmt.Errorf("wrong selection: choose a file")
	}

	b.SelectFile(f)

	// FIXME: make it more generic
	subs, err := s.providers[0].Query(ctx, f.Name)
	if err != nil {
		return nil, fmt.Errorf("error listing subtitles: %v", err)
	}

	b.SetSubtitles(subs)
	return subs, nil
}

func (s *Sub) handleDownload(ctx context.Context, b *bot.Bot, msg *bot.Message, args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("choose something")
	}

	idx, err := strconv.ParseInt(args[0], 10, 32)
	if err != nil {
		return fmt.Errorf("wrong selection: %v", err)
	}

	subs := b.Subtitles()
	if len(subs) < int(idx) {
		return fmt.Errorf("wrong selection: no such idx")
	}

	sub := subs[idx-1]
	rc, err := s.providers[0].Download(ctx, sub)
	if err != nil {
		return fmt.Errorf("error downloading subtitle: %v", err)
	}
	defer rc.Close()

	f := b.SelectedFile()
	ext := filepath.Ext(f.Name)
	fname := strings.TrimSuffix(f.Name, ext)
	fname = fmt.Sprintf("%v.srt", fname)

	err = b.UploadFile(ctx, rc, fname, f.ParentID)
	if err != nil {
		return fmt.Errorf("error uploading subtitle: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before><commit_msg>Refactor Weights.Scale().<commit_after><|endoftext|>