<commit_before>package aranGO

import (
	"errors"
  "strconv"
)

// Aql query
type Query struct {
	// mandatory
	Aql string `json:"query,omitempty"`
	//Optional values
	Batch    int                    `json:"batchSize,omitempty"`
	Count    bool                   `json:"count,omitempty"`
	BindVars map[string]interface{} `json:"bindVars,omitempty"`
	Options  map[string]interface{} `json:"options,omitempty"`
  // opetions fullCount bool
  // Note that the fullCount sub-attribute will only be present in the result if the query has a LIMIT clause and the LIMIT clause is actually used in the query.

	// Control
	Validate bool   `json:"-"`
	ErrorMsg string `json:"errorMessage,omitempty"`
}

func NewQuery(query string) *Query {
  var q Query
  // alocate maps
  q.Options = make(map[string]interface{})
  q.BindVars= make(map[string]interface{})

	if query == "" {
		return &q
	} else {
		q.Aql = query
		return &q
	}
}

func (q *Query) Modify(query string) error {
	if query == "" {
		return errors.New("query must not be empty")
	} else {
		q.Aql = query
		return nil
	}
}

// Validate query before execution
func (q *Query) MustCheck() {
	q.Validate = true
	return
}

type AqlStructer interface{
  Generate()     string
}

// Basic structure
type AqlStruct struct {
  // main loop var
  main        string
  list        string
  lines       []AqlStructer
  vars        map[string]string
  // Return
  // could be string or AqlStruct
  //View        `json:"view"`
}

func (aq *AqlStruct) For(obj string) *AqlStruct{
  aq.main = obj
  return aq
}

func (aq *AqlStruct) In(list string) *AqlStruct {
  aq.list = list
  return aq
}

func (aq *AqlStruct) Generate() string{
  q:= "FOR "+aq.main+" IN "+aq.list

  for _,line :=range(aq.lines){
    q+= line.Generate()
  }

  return q
}

// Custom struct while I implement more strutures
func (aq *AqlStruct) Custom(code string) *AqlStruct{
  var c Custom
  c.Code = code
  aq.lines = append(aq.lines,c)
  return aq
}

type Custom struct {
  Code string
}

func (c Custom) Generate() string {
  return `
  `+c.Code
}

func (aq *AqlStruct) Loop(v string,list string) *AqlStruct {
  var l Loop
  if v != "" {
    l.Var = v
    l.List = list
  }
  aq.lines = append(aq.lines,l)
  return aq
}

func (aq *AqlStruct) Return(v map[string]interface{}) *AqlStruct {
  var vie View
  vie = v
  aq.lines = append(aq.lines,vie)
  return aq
}

func (aq *AqlStruct) Group(gs map[string]Var,into string) *AqlStruct{
  if gs != nil {
    var c Collects
    c.Collect = gs
    if into != ""{
      c.Gro = into
    }
    aq.lines = append(aq.lines,c)
  }
  return aq
}

func (aq *AqlStruct) Filter(key string,values []Pair) *AqlStruct{
  var fil Filters
  if key != "" && values != nil{
    fil.Key = key
    fil.Filter = values
    aq.lines = append(aq.lines,fil)
  }
  return aq
}

type View map[string]interface{}

func (v View) Generate() string{
  q:= `
RETURN { `
  i:=0
  for key,inte := range(v) {
    q += key+":"
    switch inte.(type) {
      case Var:
        q += inte.(Var).Obj+"."+inte.(Var).Name
      case string:
        q += "'"+inte.(string)+"'"
      case int:
        q += strconv.Itoa(inte.(int))
      case int32:
        q += strconv.FormatInt(inte.(int64),10)
      case int64:
        q += strconv.FormatInt(inte.(int64),10)
      case AqlStruct,*AqlStruct:
        q += "( "+inte.(*AqlStruct).Generate()+" )"
    }
    if len(v)-1 != i {
      q+=","
    }
    i++
  }
  q+= " }"
  return q
}

type Collects struct{
  // COLLECT key = Obj.Var,..., INTO Gro
  Collect map[string]Var `json:"collect"`
  Gro   string              `json:"group"`
}

type Group struct{
  Obj   string  `json:"obj"`
  Var   string  `json:"var"`
}

func (c Collects) Generate() string{
  if c.Collect == nil {
    return ""
  }
  q:= `
COLLECT `
  i:= 0
  for key,group := range(c.Collect){
    if i == len(c.Collect)-1 {
      q += key +"="+group.Obj+"."+group.Name
    }

    if i < len(c.Collect)-1 {
      q += key +"="+group.Obj+"."+group.Name+","
    }

    i++
  }
  if c.Gro != ""{
    q += " INTO "+c.Gro
  }
  return q
}

type Limits struct{
  Skip   int64 `json:"skip"`
  Limit  int64 `json:"limit"`
}

func (l Limits) Generate() string {
  skip := strconv.FormatInt(l.Skip,10)
  limit:= strconv.FormatInt(l.Limit,10)
  li := `
LIMIT `+skip+`,`+limit
  return li
}

func (aq *AqlStruct) Limit(skip,limit int64) *AqlStruct{
  var l Limits
  l.Skip = skip
  l.Limit = limit
  aq.lines = append(aq.lines,l)
  return aq
}

func (aq *AqlStruct) Let(v string,i interface{}) *AqlStruct{
  // validate type
  switch i.(type){
    case string:
    case *AqlStruct:
    default:
      return aq
  }

  var f Lets
  if v != ""{
    f.Var = v
    f.Comm = i
  }else{
     return aq
  }
  aq.lines = append(aq.lines,f)
  return aq
}

type Lets struct {
  Var     string         `json:"var"`
  Comm    interface{}    `json:"comm"`
}

func (l Lets) Generate() string {
  q := `
LET `+l.Var+` = (`
  switch l.Comm.(type) {
    case string:
        q += l.Comm.(string)
    case *AqlStruct:
        q += l.Comm.(*AqlStruct).Generate()
  }
  q += `)`
  return q
}

type Filters struct {
  Key    string  `json:"key"`
  Filter []Pair  `json:"conditions"`
}

type Pair struct {
  Obj     string      `json:"obj"`
  Logic   string      `json:"op"`
  Value   interface{} `json:"val"`
}

func (f Filters) Generate() string{
  // check if filters available
  if len(f.Filter) == 0 {
    return ""
  }
  var oper      string

  lenmap := 0
  q := ""

  if f.Filter == nil{
    return ""
  }

  pairs := f.Filter
  key   := f.Key
  // iterate over filters
  // first
  q += `
FILTER ( `
  oper = "||"

  for i,pair := range(pairs){
    if i == len(pairs) -1 {
      oper = ""
    }
    switch pair.Value.(type) {
      case bool:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.FormatBool(pair.Value.(bool))+" "+oper
      case int:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.Itoa(pair.Value.(int))+" "+oper
      case int64:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.FormatInt(pair.Value.(int64),10)+" "+oper
      case string:
        q += key+"."+pair.Obj+" "+pair.Logic+" '"+pair.Value.(string)+"' "+oper
      case float32,float64:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.FormatFloat(pair.Value.(float64),'f',6,64)+" "+oper
      case Var:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+pair.Value.(Var).Obj+"."+pair.Value.(Var).Name+" "+oper
    }
    if i == len(pairs)-1{
      q += ")"
    }
  }
  // next key
  lenmap++
  return q
}

type Loop struct {
  Var  string
  List string
}

func (l Loop) Generate() string{
  q := `
FOR `+l.Var+` IN `+l.List
  return q
}

// Variable into document
type Var  struct {
  Obj     string      `json:"obj"`
  Name    string      `json:"name"`
}

type AqlUserFunction struct {
  Name string `json:"name"`
  Code string `json:"code"`
  Deterministic bool `json:"isDeterministic"`
}

func NewAqlFunction(name string,code string,deter bool) *AqlUserFunction{
  var aqf AqlUserFunction
  if name != "" && code != ""{
    aqf.Name = name
    aqf.Code = code
    return &aqf
  }
  return nil
}

// Register new user function
func (a *AqlUserFunction) Register(db Database) error{
  res, err := db.send("aqlfunction","","POST",a,nil,nil)
  if err != nil {
    return err
  }

  switch res.Status() {
    case 200,201:
       return nil
    case 400:
      return errors.New("Failed to create aqlfunction")
    default:
      return nil
  }
}

func (a *AqlUserFunction) Delete(db Database,name string) error{
  res, err := db.send("aqlfunction",name,"DELETE",a,nil,nil)
  if err != nil {
    return err
  }

  switch res.Status() {
    case 200,201:
       return nil
    case 400:
      return errors.New("Invalid name")
    case 404:
      return errors.New("Function don't exist")
    default:
      return nil
  }
}

type FullTextSearch struct {
  Code  string
  List  string
}

// Generate AQL Code to do fulltext search over multiple indexes using UNION to join lists
func FullText(words []string,indexes []string,col string) *FullTextSearch{
  var t FullTextSearch
  var i,j int

  lindex := 0
  for i = 0 ; i<len(words) ; i++ {
    for j = 0 ; j <len(indexes) ; j++ {
      // add list to array
      lindex++
      t.Code += `LET l`+strconv.Itoa(lindex)+`=(FOR i IN FULLTEXT(`+col+`,"`+indexes[j]+`","prefix:`+words[i]+`,|`+words[i]+`") RETURN i)
      `
    }
  }
  t.List = "UNION("
  for i = 1 ; i < lindex ;i++{
    t.List +="l"+strconv.Itoa(i)+","
  }

  t.List +="l"+strconv.Itoa(lindex)+")"

  return &t
}

<commit_msg>Union_disctinct<commit_after>package aranGO

import (
	"errors"
  "strconv"
)

// Aql query
type Query struct {
	// mandatory
	Aql string `json:"query,omitempty"`
	//Optional values
	Batch    int                    `json:"batchSize,omitempty"`
	Count    bool                   `json:"count,omitempty"`
	BindVars map[string]interface{} `json:"bindVars,omitempty"`
	Options  map[string]interface{} `json:"options,omitempty"`
  // opetions fullCount bool
  // Note that the fullCount sub-attribute will only be present in the result if the query has a LIMIT clause and the LIMIT clause is actually used in the query.

	// Control
	Validate bool   `json:"-"`
	ErrorMsg string `json:"errorMessage,omitempty"`
}

func NewQuery(query string) *Query {
  var q Query
  // alocate maps
  q.Options = make(map[string]interface{})
  q.BindVars= make(map[string]interface{})

	if query == "" {
		return &q
	} else {
		q.Aql = query
		return &q
	}
}

func (q *Query) Modify(query string) error {
	if query == "" {
		return errors.New("query must not be empty")
	} else {
		q.Aql = query
		return nil
	}
}

// Validate query before execution
func (q *Query) MustCheck() {
	q.Validate = true
	return
}

type AqlStructer interface{
  Generate()     string
}

// Basic structure
type AqlStruct struct {
  // main loop var
  main        string
  list        string
  lines       []AqlStructer
  vars        map[string]string
  // Return
  // could be string or AqlStruct
  //View        `json:"view"`
}

func (aq *AqlStruct) For(obj string) *AqlStruct{
  aq.main = obj
  return aq
}

func (aq *AqlStruct) In(list string) *AqlStruct {
  aq.list = list
  return aq
}

func (aq *AqlStruct) Generate() string{
  q:= "FOR "+aq.main+" IN "+aq.list

  for _,line :=range(aq.lines){
    q+= line.Generate()
  }

  return q
}

// Custom struct while I implement more strutures
func (aq *AqlStruct) Custom(code string) *AqlStruct{
  var c Custom
  c.Code = code
  aq.lines = append(aq.lines,c)
  return aq
}

type Custom struct {
  Code string
}

func (c Custom) Generate() string {
  return `
  `+c.Code
}

func (aq *AqlStruct) Loop(v string,list string) *AqlStruct {
  var l Loop
  if v != "" {
    l.Var = v
    l.List = list
  }
  aq.lines = append(aq.lines,l)
  return aq
}

func (aq *AqlStruct) Return(v map[string]interface{}) *AqlStruct {
  var vie View
  vie = v
  aq.lines = append(aq.lines,vie)
  return aq
}

func (aq *AqlStruct) Group(gs map[string]Var,into string) *AqlStruct{
  if gs != nil {
    var c Collects
    c.Collect = gs
    if into != ""{
      c.Gro = into
    }
    aq.lines = append(aq.lines,c)
  }
  return aq
}

func (aq *AqlStruct) Filter(key string,values []Pair) *AqlStruct{
  var fil Filters
  if key != "" && values != nil{
    fil.Key = key
    fil.Filter = values
    aq.lines = append(aq.lines,fil)
  }
  return aq
}

type View map[string]interface{}

func (v View) Generate() string{
  q:= `
RETURN { `
  i:=0
  for key,inte := range(v) {
    q += key+":"
    switch inte.(type) {
      case Var:
        q += inte.(Var).Obj+"."+inte.(Var).Name
      case string:
        q += "'"+inte.(string)+"'"
      case int:
        q += strconv.Itoa(inte.(int))
      case int32:
        q += strconv.FormatInt(inte.(int64),10)
      case int64:
        q += strconv.FormatInt(inte.(int64),10)
      case AqlStruct,*AqlStruct:
        q += "( "+inte.(*AqlStruct).Generate()+" )"
    }
    if len(v)-1 != i {
      q+=","
    }
    i++
  }
  q+= " }"
  return q
}

type Collects struct{
  // COLLECT key = Obj.Var,..., INTO Gro
  Collect map[string]Var `json:"collect"`
  Gro   string              `json:"group"`
}

type Group struct{
  Obj   string  `json:"obj"`
  Var   string  `json:"var"`
}

func (c Collects) Generate() string{
  if c.Collect == nil {
    return ""
  }
  q:= `
COLLECT `
  i:= 0
  for key,group := range(c.Collect){
    if i == len(c.Collect)-1 {
      q += key +"="+group.Obj+"."+group.Name
    }

    if i < len(c.Collect)-1 {
      q += key +"="+group.Obj+"."+group.Name+","
    }

    i++
  }
  if c.Gro != ""{
    q += " INTO "+c.Gro
  }
  return q
}

type Limits struct{
  Skip   int64 `json:"skip"`
  Limit  int64 `json:"limit"`
}

func (l Limits) Generate() string {
  skip := strconv.FormatInt(l.Skip,10)
  limit:= strconv.FormatInt(l.Limit,10)
  li := `
LIMIT `+skip+`,`+limit
  return li
}

func (aq *AqlStruct) Limit(skip,limit int64) *AqlStruct{
  var l Limits
  l.Skip = skip
  l.Limit = limit
  aq.lines = append(aq.lines,l)
  return aq
}

func (aq *AqlStruct) Let(v string,i interface{}) *AqlStruct{
  // validate type
  switch i.(type){
    case string:
    case *AqlStruct:
    default:
      return aq
  }

  var f Lets
  if v != ""{
    f.Var = v
    f.Comm = i
  }else{
     return aq
  }
  aq.lines = append(aq.lines,f)
  return aq
}

type Lets struct {
  Var     string         `json:"var"`
  Comm    interface{}    `json:"comm"`
}

func (l Lets) Generate() string {
  q := `
LET `+l.Var+` = (`
  switch l.Comm.(type) {
    case string:
        q += l.Comm.(string)
    case *AqlStruct:
        q += l.Comm.(*AqlStruct).Generate()
  }
  q += `)`
  return q
}

type Filters struct {
  Key    string  `json:"key"`
  Filter []Pair  `json:"conditions"`
}

type Pair struct {
  Obj     string      `json:"obj"`
  Logic   string      `json:"op"`
  Value   interface{} `json:"val"`
}

func (f Filters) Generate() string{
  // check if filters available
  if len(f.Filter) == 0 {
    return ""
  }
  var oper      string

  lenmap := 0
  q := ""

  if f.Filter == nil{
    return ""
  }

  pairs := f.Filter
  key   := f.Key
  // iterate over filters
  // first
  q += `
FILTER ( `
  oper = "||"

  for i,pair := range(pairs){
    if i == len(pairs) -1 {
      oper = ""
    }
    switch pair.Value.(type) {
      case bool:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.FormatBool(pair.Value.(bool))+" "+oper
      case int:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.Itoa(pair.Value.(int))+" "+oper
      case int64:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.FormatInt(pair.Value.(int64),10)+" "+oper
      case string:
        q += key+"."+pair.Obj+" "+pair.Logic+" '"+pair.Value.(string)+"' "+oper
      case float32,float64:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+strconv.FormatFloat(pair.Value.(float64),'f',6,64)+" "+oper
      case Var:
        q += key+"."+pair.Obj+" "+pair.Logic+" "+pair.Value.(Var).Obj+"."+pair.Value.(Var).Name+" "+oper
    }
    if i == len(pairs)-1{
      q += ")"
    }
  }
  // next key
  lenmap++
  return q
}

type Loop struct {
  Var  string
  List string
}

func (l Loop) Generate() string{
  q := `
FOR `+l.Var+` IN `+l.List
  return q
}

// Variable into document
type Var  struct {
  Obj     string      `json:"obj"`
  Name    string      `json:"name"`
}

type AqlUserFunction struct {
  Name string `json:"name"`
  Code string `json:"code"`
  Deterministic bool `json:"isDeterministic"`
}

func NewAqlFunction(name string,code string,deter bool) *AqlUserFunction{
  var aqf AqlUserFunction
  if name != "" && code != ""{
    aqf.Name = name
    aqf.Code = code
    return &aqf
  }
  return nil
}

// Register new user function
func (a *AqlUserFunction) Register(db Database) error{
  res, err := db.send("aqlfunction","","POST",a,nil,nil)
  if err != nil {
    return err
  }

  switch res.Status() {
    case 200,201:
       return nil
    case 400:
      return errors.New("Failed to create aqlfunction")
    default:
      return nil
  }
}

func (a *AqlUserFunction) Delete(db Database,name string) error{
  res, err := db.send("aqlfunction",name,"DELETE",a,nil,nil)
  if err != nil {
    return err
  }

  switch res.Status() {
    case 200,201:
       return nil
    case 400:
      return errors.New("Invalid name")
    case 404:
      return errors.New("Function don't exist")
    default:
      return nil
  }
}

type FullTextSearch struct {
  Code  string
  List  string
}

// Generate AQL Code to do fulltext search over multiple indexes using UNION to join lists
func FullText(words []string,indexes []string,col string) *FullTextSearch{
  var t FullTextSearch
  var i,j int

  lindex := 0
  for i = 0 ; i<len(words) ; i++ {
    for j = 0 ; j <len(indexes) ; j++ {
      // add list to array
      lindex++
      t.Code += `LET l`+strconv.Itoa(lindex)+`=(FOR i IN FULLTEXT(`+col+`,"`+indexes[j]+`","prefix:`+words[i]+`,|`+words[i]+`") RETURN i)
      `
    }
  }
  t.List = "UNION_DISTINCT("
  for i = 1 ; i < lindex ;i++{
    t.List +="l"+strconv.Itoa(i)+","
  }

  t.List +="l"+strconv.Itoa(lindex)+")"

  return &t
}

<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"

	"github.com/justsocialapps/holmes/analytics"
	"github.com/justsocialapps/holmes/assets"
	"github.com/justsocialapps/holmes/publisher"
	"github.com/justsocialapps/holmes/tracker"
)

const version string = "1.7.0-dev"

//go:generate scripts/prepare_assets.sh
//go:generate go run scripts/include_assets.go

func provideTrackingChannel(trackingChannel chan<- *tracker.TrackingObject, handler func(trackingChannel chan<- *tracker.TrackingObject, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		handler(trackingChannel, w, r)
	}
}

func startServer(host string, port *string) {
	listener, err := net.Listen("tcp", host+":"+*port)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Holmes running on " + host + ":" + *port)
	log.Println(assets.Bannertxt)

	err = http.Serve(listener, nil)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	var protocol = flag.String("protocol", "https", "The protocol used to serve Holmes resources ('http' or 'https')")
	var host = flag.String("host", "localhost", "The host name used to reach Holmes")
	var proxyPort = flag.String("proxyPort", "3001", "The TCP port for reaching Holmes if Holmes is operated behind a reverse proxy.")
	var proxyPath = flag.String("proxyPath", "", "The base path for reaching Holmes if Holmes is operated behind a reverse proxy.")
	var listenPort = flag.String("listenPort", "3001", "The TCP port that Holmes listens on")
	var kafkaHost = flag.String("kafkaHost", "localhost:9092", "The Kafka host to consume messages from")
	var logfileName = flag.String("logfile", "", "The file to log messages to")
	var printVersion = flag.Bool("version", false, "Print Holmes version and exit")
	flag.Parse()

	if *printVersion {
		fmt.Println(version)
		return
	}

	var logFile *os.File
	if *logfileName == "" {
		logFile = os.Stdout
	} else {
		var err error
		logFile, err = os.OpenFile(*logfileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatal(err)
		}
	}
	log.SetOutput(logFile)

	baseURL := *protocol + "://" + *host
	if !(*protocol == "https" && *proxyPort == "443") && !(*protocol == "http" && *proxyPort == "80") {
		baseURL = baseURL + ":" + *proxyPort
	}
	baseURL = baseURL + *proxyPath

	trackingChannel := make(chan *tracker.TrackingObject, 10)
	http.HandleFunc("/track", provideTrackingChannel(trackingChannel, tracker.Track))
	http.HandleFunc("/analytics.js", analytics.Analytics(baseURL))
	go publisher.Publish(trackingChannel, kafkaHost, "tracking")

	startServer("localhost", listenPort)
}
<commit_msg>Fix import and add some logging<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"

	"github.com/justsocialapps/holmes/analytics"
	"github.com/justsocialapps/holmes/assets"
	"github.com/justsocialapps/holmes/publisher"
	"github.com/justsocialapps/holmes/tracker"
	"gopkg.in/Shopify/sarama.v1"
)

const version string = "1.7.0-dev"

//go:generate scripts/prepare_assets.sh
//go:generate go run scripts/include_assets.go

func provideTrackingChannel(trackingChannel chan<- *tracker.TrackingObject, handler func(trackingChannel chan<- *tracker.TrackingObject, w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		handler(trackingChannel, w, r)
	}
}

func startServer(host string, port *string) {
	listener, err := net.Listen("tcp", host+":"+*port)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("Holmes running on " + host + ":" + *port)
	log.Println(assets.Bannertxt)

	err = http.Serve(listener, nil)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	var protocol = flag.String("protocol", "https", "The protocol used to serve Holmes resources ('http' or 'https')")
	var host = flag.String("host", "localhost", "The host name used to reach Holmes")
	var proxyPort = flag.String("proxyPort", "3001", "The TCP port for reaching Holmes if Holmes is operated behind a reverse proxy.")
	var proxyPath = flag.String("proxyPath", "", "The base path for reaching Holmes if Holmes is operated behind a reverse proxy.")
	var listenPort = flag.String("listenPort", "3001", "The TCP port that Holmes listens on")
	var kafkaHost = flag.String("kafkaHost", "localhost:9092", "The Kafka host to consume messages from")
	var logfileName = flag.String("logfile", "", "The file to log messages to")
	var printVersion = flag.Bool("version", false, "Print Holmes version and exit")
	flag.Parse()

	if *printVersion {
		fmt.Println(version)
		return
	}

	var logFile *os.File
	if *logfileName == "" {
		logFile = os.Stdout
	} else {
		var err error
		logFile, err = os.OpenFile(*logfileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
		if err != nil {
			log.Fatal(err)
		}
	}
	log.SetOutput(logFile)
	sarama.Logger = log.New(logFile, "[Sarama] ", log.LstdFlags)

	baseURL := *protocol + "://" + *host
	if !(*protocol == "https" && *proxyPort == "443") && !(*protocol == "http" && *proxyPort == "80") {
		baseURL = baseURL + ":" + *proxyPort
	}
	baseURL = baseURL + *proxyPath

	trackingChannel := make(chan *tracker.TrackingObject, 10)
	http.HandleFunc("/track", provideTrackingChannel(trackingChannel, tracker.Track))
	http.HandleFunc("/analytics.js", analytics.Analytics(baseURL))
	go publisher.Publish(trackingChannel, kafkaHost, "tracking")

	startServer("localhost", listenPort)
}
<|endoftext|>


<commit_before>package request

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestValidateSingleInstance(t *testing.T) {
	i1 := getInstance()
	i2 := getInstance()

	assert.NotNil(t, i1, "Should not be nil")
	assert.NotNil(t, i2, "Should not be nil")
	assert.True(t, i1 == i2, "Should be the same instance")
}

func TestValidateMultipleInstances(t *testing.T) {
	i1 := getInstance()
	instance = nil
	i2 := getInstance()

	assert.NotNil(t, i1, "Should not be nil")
	assert.NotNil(t, i2, "Should not be nil")
	assert.False(t, i1 == i2, "Should be different instances")
}
<commit_msg>Added more tests.<commit_after>package request

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

const (
	user    = "test"
	pass    = "12345"
	hash    = "Z29sYW5ndGVzdA=="
	testUrl = "https://www.google.com"
)

func TestValidateSingleInstance(t *testing.T) {
	i1 := getInstance()
	i2 := getInstance()

	assert.NotNil(t, i1, "Should not be nil")
	assert.NotNil(t, i2, "Should not be nil")
	assert.True(t, i1 == i2, "Should be the same instance")
}

func TestValidateMultipleInstances(t *testing.T) {
	i1 := getInstance()
	instance = nil
	i2 := getInstance()

	assert.NotNil(t, i1, "Should not be nil")
	assert.NotNil(t, i2, "Should not be nil")
	assert.False(t, i1 == i2, "Should be different instances")
}

func TestValidateNewAuth(t *testing.T) {
	auth := NewAuth(user, pass, hash)

	assert.Equal(t, user, auth.Username, "Should equal username")
	assert.Equal(t, pass, auth.Password, "Should equal password")
	assert.Equal(t, hash, auth.Bearer, "Should equal token")
}

func TestValidateDefaultHttpClientTimeout(t *testing.T) {
	r := New()

	assert.Equal(t, 30*time.Second, r.client.Timeout, "Should default to 30 seconds")
}

func TestValidateOverridingHttpClientTimeout(t *testing.T) {
	// REMARKS: Override timeout value to 45 seconds
	r := New(45)

	assert.Equal(t, 45*time.Second, r.client.Timeout, "Should equals 45 seconds")
}
<|endoftext|>


<commit_before>package mpb

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/vbauerster/mpb/v4/decor"
)

// Filler interface.
// Bar renders by calling Filler's Fill method. You can literally have
// any bar kind, by implementing this interface and passing it to the
// Add method.
type Filler interface {
	Fill(w io.Writer, width int, stat *decor.Statistics)
}

// FillerFunc is function type adapter to convert function into Filler.
type FillerFunc func(w io.Writer, width int, stat *decor.Statistics)

func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
	f(w, width, stat)
}

// Bar represents a progress Bar.
type Bar struct {
	priority int
	index    int

	runningBar    *Bar
	cacheState    *bState
	operateState  chan func(*bState)
	int64Ch       chan int64
	boolCh        chan bool
	frameReaderCh chan *frameReader
	syncTableCh   chan [][]chan int

	// done is closed by Bar's goroutine, after cacheState is written
	done chan struct{}
	// shutdown is closed from master Progress goroutine only
	shutdown chan struct{}
}

type (
	bState struct {
		filler                 Filler
		extender               Filler
		id                     int
		width                  int
		alignment              int
		total                  int64
		current                int64
		trimSpace              bool
		toComplete             bool
		removeOnComplete       bool
		barClearOnComplete     bool
		completeFlushed        bool
		aDecorators            []decor.Decorator
		pDecorators            []decor.Decorator
		amountReceivers        []decor.AmountReceiver
		shutdownListeners      []decor.ShutdownListener
		bufP, bufB, bufA, bufE *bytes.Buffer
		panicMsg               string

		// following options are assigned to the *Bar
		priority   int
		runningBar *Bar
	}
	frameReader struct {
		io.Reader
		extendedLines    int
		toShutdown       bool
		removeOnComplete bool
	}
)

func newBar(
	ctx context.Context,
	wg *sync.WaitGroup,
	filler Filler,
	id, width int,
	total int64,
	options ...BarOption,
) *Bar {
	if total <= 0 {
		total = time.Now().Unix()
	}

	s := &bState{
		filler:   filler,
		id:       id,
		priority: id,
		width:    width,
		total:    total,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	s.bufP = bytes.NewBuffer(make([]byte, 0, width))
	s.bufB = bytes.NewBuffer(make([]byte, 0, width))
	s.bufA = bytes.NewBuffer(make([]byte, 0, width))
	if s.extender != nil {
		s.bufE = bytes.NewBuffer(make([]byte, 0, width))
	}

	b := &Bar{
		priority:      s.priority,
		runningBar:    s.runningBar,
		operateState:  make(chan func(*bState)),
		int64Ch:       make(chan int64),
		boolCh:        make(chan bool),
		frameReaderCh: make(chan *frameReader, 1),
		syncTableCh:   make(chan [][]chan int),
		done:          make(chan struct{}),
		shutdown:      make(chan struct{}),
	}

	if b.runningBar != nil {
		b.priority = b.runningBar.priority
	}

	go b.serve(ctx, wg, s)
	return b
}

// RemoveAllPrependers removes all prepend functions.
func (b *Bar) RemoveAllPrependers() {
	select {
	case b.operateState <- func(s *bState) { s.pDecorators = nil }:
	case <-b.done:
	}
}

// RemoveAllAppenders removes all append functions.
func (b *Bar) RemoveAllAppenders() {
	select {
	case b.operateState <- func(s *bState) { s.aDecorators = nil }:
	case <-b.done:
	}
}

// ProxyReader wraps r with metrics required for progress tracking.
func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
	if r == nil {
		panic("expect io.Reader, got nil")
	}
	rc, ok := r.(io.ReadCloser)
	if !ok {
		rc = ioutil.NopCloser(r)
	}
	return &proxyReader{rc, b, time.Now()}
}

// ID returs id of the bar.
func (b *Bar) ID() int {
	select {
	case b.operateState <- func(s *bState) { b.int64Ch <- int64(s.id) }:
		return int(<-b.int64Ch)
	case <-b.done:
		return b.cacheState.id
	}
}

// Current returns bar's current number, in other words sum of all increments.
func (b *Bar) Current() int64 {
	select {
	case b.operateState <- func(s *bState) { b.int64Ch <- s.current }:
		return <-b.int64Ch
	case <-b.done:
		return b.cacheState.current
	}
}

// SetTotal sets total dynamically.
// Set final to true, when total is known, it will trigger bar complete event.
func (b *Bar) SetTotal(total int64, final bool) bool {
	select {
	case b.operateState <- func(s *bState) {
		if total > 0 {
			s.total = total
		}
		if final {
			s.current = s.total
			s.toComplete = true
		}
	}:
		return true
	case <-b.done:
		return false
	}
}

// SetRefill sets refill, if supported by underlying Filler.
func (b *Bar) SetRefill(upto int) {
	b.operateState <- func(s *bState) {
		if f, ok := s.filler.(interface{ SetRefill(int) }); ok {
			f.SetRefill(upto)
		}
	}
}

// Increment is a shorthand for b.IncrBy(1).
func (b *Bar) Increment() {
	b.IncrBy(1)
}

// IncrBy increments progress bar by amount of n.
// wdd is optional work duration i.e. time.Since(start), which expected
// to be provided, if any ewma based decorator is used.
func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
	select {
	case b.operateState <- func(s *bState) {
		s.current += int64(n)
		if s.current >= s.total {
			s.current = s.total
			s.toComplete = true
		}
		for _, ar := range s.amountReceivers {
			ar.NextAmount(n, wdd...)
		}
	}:
	case <-b.done:
	}
}

// Completed reports whether the bar is in completed state.
func (b *Bar) Completed() bool {
	// omit select here, because primary usage of the method is for loop
	// condition, like for !bar.Completed() {...} so when toComplete=true
	// it is called once (at which time, the bar is still alive), then
	// quits the loop and never suppose to be called afterwards.
	return <-b.boolCh
}

func (b *Bar) wSyncTable() [][]chan int {
	select {
	case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }:
		return <-b.syncTableCh
	case <-b.done:
		return b.cacheState.wSyncTable()
	}
}

func (b *Bar) serve(ctx context.Context, wg *sync.WaitGroup, s *bState) {
	defer wg.Done()
	cancel := ctx.Done()
	for {
		select {
		case op := <-b.operateState:
			op(s)
		case b.boolCh <- s.toComplete:
		case <-cancel:
			s.toComplete = true
			cancel = nil
		case <-b.shutdown:
			b.cacheState = s
			close(b.done)
			for _, sl := range s.shutdownListeners {
				sl.Shutdown()
			}
			return
		}
	}
}

func (b *Bar) render(debugOut io.Writer, tw int) {
	select {
	case b.operateState <- func(s *bState) {
		defer func() {
			// recovering if user defined decorator panics for example
			if p := recover(); p != nil {
				s.panicMsg = fmt.Sprintf("panic: %v", p)
				fmt.Fprintf(debugOut, "%s %s bar id %02d %v\n", "[mpb]", time.Now(), s.id, s.panicMsg)
				b.frameReaderCh <- &frameReader{
					Reader:     strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg)),
					toShutdown: true,
				}
			}
		}()
		r := s.draw(tw)
		var extendedLines int
		if s.extender != nil {
			s.extender.Fill(s.bufE, tw, newStatistics(s))
			extendedLines = countLines(s.bufE.Bytes())
			r = io.MultiReader(r, s.bufE)
		}
		b.frameReaderCh <- &frameReader{
			Reader:           r,
			extendedLines:    extendedLines,
			toShutdown:       s.toComplete && !s.completeFlushed,
			removeOnComplete: s.removeOnComplete,
		}
		s.completeFlushed = s.toComplete
	}:
	case <-b.done:
		s := b.cacheState
		r := s.draw(tw)
		var extendedLines int
		if s.extender != nil {
			s.extender.Fill(s.bufE, tw, newStatistics(s))
			extendedLines = countLines(s.bufE.Bytes())
			r = io.MultiReader(r, s.bufE)
		}
		b.frameReaderCh <- &frameReader{
			Reader:        r,
			extendedLines: extendedLines,
		}
	}
}

func (s *bState) draw(termWidth int) io.Reader {
	if s.panicMsg != "" {
		return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
	}

	stat := newStatistics(s)

	for _, d := range s.pDecorators {
		s.bufP.WriteString(d.Decor(stat))
	}

	for _, d := range s.aDecorators {
		s.bufA.WriteString(d.Decor(stat))
	}

	if s.barClearOnComplete && s.completeFlushed {
		s.bufA.WriteByte('\n')
		return io.MultiReader(s.bufP, s.bufA)
	}

	prependCount := utf8.RuneCount(s.bufP.Bytes())
	appendCount := utf8.RuneCount(s.bufA.Bytes())

	if !s.trimSpace {
		// reserve space for edge spaces
		termWidth -= 2
		s.bufB.WriteByte(' ')
	}

	calcWidth := s.width
	if prependCount+s.width+appendCount > termWidth {
		calcWidth = termWidth - prependCount - appendCount
	}
	s.filler.Fill(s.bufB, calcWidth, stat)

	if !s.trimSpace {
		s.bufB.WriteByte(' ')
	}

	s.bufA.WriteByte('\n')
	return io.MultiReader(s.bufP, s.bufB, s.bufA)
}

func (s *bState) wSyncTable() [][]chan int {
	columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
	var pCount int
	for _, d := range s.pDecorators {
		if ch, ok := d.Sync(); ok {
			columns = append(columns, ch)
			pCount++
		}
	}
	var aCount int
	for _, d := range s.aDecorators {
		if ch, ok := d.Sync(); ok {
			columns = append(columns, ch)
			aCount++
		}
	}
	table := make([][]chan int, 2)
	table[0] = columns[0:pCount]
	table[1] = columns[pCount : pCount+aCount : pCount+aCount]
	return table
}

func newStatistics(s *bState) *decor.Statistics {
	return &decor.Statistics{
		ID:        s.id,
		Completed: s.completeFlushed,
		Total:     s.total,
		Current:   s.current,
	}
}

func countLines(b []byte) int {
	return bytes.Count(b, []byte("\n"))
}
<commit_msg>shutdown event comment<commit_after>package mpb

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"strings"
	"sync"
	"time"
	"unicode/utf8"

	"github.com/vbauerster/mpb/v4/decor"
)

// Filler interface.
// Bar renders by calling Filler's Fill method. You can literally have
// any bar kind, by implementing this interface and passing it to the
// Add method.
type Filler interface {
	Fill(w io.Writer, width int, stat *decor.Statistics)
}

// FillerFunc is function type adapter to convert function into Filler.
type FillerFunc func(w io.Writer, width int, stat *decor.Statistics)

func (f FillerFunc) Fill(w io.Writer, width int, stat *decor.Statistics) {
	f(w, width, stat)
}

// Bar represents a progress Bar.
type Bar struct {
	priority int
	index    int

	runningBar    *Bar
	cacheState    *bState
	operateState  chan func(*bState)
	int64Ch       chan int64
	boolCh        chan bool
	frameReaderCh chan *frameReader
	syncTableCh   chan [][]chan int

	// done is closed by Bar's goroutine, after cacheState is written
	done chan struct{}
	// shutdown is closed from master Progress goroutine only
	shutdown chan struct{}
}

type (
	bState struct {
		filler                 Filler
		extender               Filler
		id                     int
		width                  int
		alignment              int
		total                  int64
		current                int64
		trimSpace              bool
		toComplete             bool
		removeOnComplete       bool
		barClearOnComplete     bool
		completeFlushed        bool
		aDecorators            []decor.Decorator
		pDecorators            []decor.Decorator
		amountReceivers        []decor.AmountReceiver
		shutdownListeners      []decor.ShutdownListener
		bufP, bufB, bufA, bufE *bytes.Buffer
		panicMsg               string

		// following options are assigned to the *Bar
		priority   int
		runningBar *Bar
	}
	frameReader struct {
		io.Reader
		extendedLines    int
		toShutdown       bool
		removeOnComplete bool
	}
)

func newBar(
	ctx context.Context,
	wg *sync.WaitGroup,
	filler Filler,
	id, width int,
	total int64,
	options ...BarOption,
) *Bar {
	if total <= 0 {
		total = time.Now().Unix()
	}

	s := &bState{
		filler:   filler,
		id:       id,
		priority: id,
		width:    width,
		total:    total,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	s.bufP = bytes.NewBuffer(make([]byte, 0, width))
	s.bufB = bytes.NewBuffer(make([]byte, 0, width))
	s.bufA = bytes.NewBuffer(make([]byte, 0, width))
	if s.extender != nil {
		s.bufE = bytes.NewBuffer(make([]byte, 0, width))
	}

	b := &Bar{
		priority:      s.priority,
		runningBar:    s.runningBar,
		operateState:  make(chan func(*bState)),
		int64Ch:       make(chan int64),
		boolCh:        make(chan bool),
		frameReaderCh: make(chan *frameReader, 1),
		syncTableCh:   make(chan [][]chan int),
		done:          make(chan struct{}),
		shutdown:      make(chan struct{}),
	}

	if b.runningBar != nil {
		b.priority = b.runningBar.priority
	}

	go b.serve(ctx, wg, s)
	return b
}

// RemoveAllPrependers removes all prepend functions.
func (b *Bar) RemoveAllPrependers() {
	select {
	case b.operateState <- func(s *bState) { s.pDecorators = nil }:
	case <-b.done:
	}
}

// RemoveAllAppenders removes all append functions.
func (b *Bar) RemoveAllAppenders() {
	select {
	case b.operateState <- func(s *bState) { s.aDecorators = nil }:
	case <-b.done:
	}
}

// ProxyReader wraps r with metrics required for progress tracking.
func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
	if r == nil {
		panic("expect io.Reader, got nil")
	}
	rc, ok := r.(io.ReadCloser)
	if !ok {
		rc = ioutil.NopCloser(r)
	}
	return &proxyReader{rc, b, time.Now()}
}

// ID returs id of the bar.
func (b *Bar) ID() int {
	select {
	case b.operateState <- func(s *bState) { b.int64Ch <- int64(s.id) }:
		return int(<-b.int64Ch)
	case <-b.done:
		return b.cacheState.id
	}
}

// Current returns bar's current number, in other words sum of all increments.
func (b *Bar) Current() int64 {
	select {
	case b.operateState <- func(s *bState) { b.int64Ch <- s.current }:
		return <-b.int64Ch
	case <-b.done:
		return b.cacheState.current
	}
}

// SetTotal sets total dynamically.
// Set final to true, when total is known, it will trigger bar complete event.
func (b *Bar) SetTotal(total int64, final bool) bool {
	select {
	case b.operateState <- func(s *bState) {
		if total > 0 {
			s.total = total
		}
		if final {
			s.current = s.total
			s.toComplete = true
		}
	}:
		return true
	case <-b.done:
		return false
	}
}

// SetRefill sets refill, if supported by underlying Filler.
func (b *Bar) SetRefill(upto int) {
	b.operateState <- func(s *bState) {
		if f, ok := s.filler.(interface{ SetRefill(int) }); ok {
			f.SetRefill(upto)
		}
	}
}

// Increment is a shorthand for b.IncrBy(1).
func (b *Bar) Increment() {
	b.IncrBy(1)
}

// IncrBy increments progress bar by amount of n.
// wdd is optional work duration i.e. time.Since(start), which expected
// to be provided, if any ewma based decorator is used.
func (b *Bar) IncrBy(n int, wdd ...time.Duration) {
	select {
	case b.operateState <- func(s *bState) {
		s.current += int64(n)
		if s.current >= s.total {
			s.current = s.total
			s.toComplete = true
		}
		for _, ar := range s.amountReceivers {
			ar.NextAmount(n, wdd...)
		}
	}:
	case <-b.done:
	}
}

// Completed reports whether the bar is in completed state.
func (b *Bar) Completed() bool {
	// omit select here, because primary usage of the method is for loop
	// condition, like for !bar.Completed() {...} so when toComplete=true
	// it is called once (at which time, the bar is still alive), then
	// quits the loop and never suppose to be called afterwards.
	return <-b.boolCh
}

func (b *Bar) wSyncTable() [][]chan int {
	select {
	case b.operateState <- func(s *bState) { b.syncTableCh <- s.wSyncTable() }:
		return <-b.syncTableCh
	case <-b.done:
		return b.cacheState.wSyncTable()
	}
}

func (b *Bar) serve(ctx context.Context, wg *sync.WaitGroup, s *bState) {
	defer wg.Done()
	cancel := ctx.Done()
	for {
		select {
		case op := <-b.operateState:
			op(s)
		case b.boolCh <- s.toComplete:
		case <-cancel:
			s.toComplete = true
			cancel = nil
		case <-b.shutdown:
			b.cacheState = s
			close(b.done)
			// Notifying decorators about shutdown event
			for _, sl := range s.shutdownListeners {
				sl.Shutdown()
			}
			return
		}
	}
}

func (b *Bar) render(debugOut io.Writer, tw int) {
	select {
	case b.operateState <- func(s *bState) {
		defer func() {
			// recovering if user defined decorator panics for example
			if p := recover(); p != nil {
				s.panicMsg = fmt.Sprintf("panic: %v", p)
				fmt.Fprintf(debugOut, "%s %s bar id %02d %v\n", "[mpb]", time.Now(), s.id, s.panicMsg)
				b.frameReaderCh <- &frameReader{
					Reader:     strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg)),
					toShutdown: true,
				}
			}
		}()
		r := s.draw(tw)
		var extendedLines int
		if s.extender != nil {
			s.extender.Fill(s.bufE, tw, newStatistics(s))
			extendedLines = countLines(s.bufE.Bytes())
			r = io.MultiReader(r, s.bufE)
		}
		b.frameReaderCh <- &frameReader{
			Reader:           r,
			extendedLines:    extendedLines,
			toShutdown:       s.toComplete && !s.completeFlushed,
			removeOnComplete: s.removeOnComplete,
		}
		s.completeFlushed = s.toComplete
	}:
	case <-b.done:
		s := b.cacheState
		r := s.draw(tw)
		var extendedLines int
		if s.extender != nil {
			s.extender.Fill(s.bufE, tw, newStatistics(s))
			extendedLines = countLines(s.bufE.Bytes())
			r = io.MultiReader(r, s.bufE)
		}
		b.frameReaderCh <- &frameReader{
			Reader:        r,
			extendedLines: extendedLines,
		}
	}
}

func (s *bState) draw(termWidth int) io.Reader {
	if s.panicMsg != "" {
		return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg))
	}

	stat := newStatistics(s)

	for _, d := range s.pDecorators {
		s.bufP.WriteString(d.Decor(stat))
	}

	for _, d := range s.aDecorators {
		s.bufA.WriteString(d.Decor(stat))
	}

	if s.barClearOnComplete && s.completeFlushed {
		s.bufA.WriteByte('\n')
		return io.MultiReader(s.bufP, s.bufA)
	}

	prependCount := utf8.RuneCount(s.bufP.Bytes())
	appendCount := utf8.RuneCount(s.bufA.Bytes())

	if !s.trimSpace {
		// reserve space for edge spaces
		termWidth -= 2
		s.bufB.WriteByte(' ')
	}

	calcWidth := s.width
	if prependCount+s.width+appendCount > termWidth {
		calcWidth = termWidth - prependCount - appendCount
	}
	s.filler.Fill(s.bufB, calcWidth, stat)

	if !s.trimSpace {
		s.bufB.WriteByte(' ')
	}

	s.bufA.WriteByte('\n')
	return io.MultiReader(s.bufP, s.bufB, s.bufA)
}

func (s *bState) wSyncTable() [][]chan int {
	columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
	var pCount int
	for _, d := range s.pDecorators {
		if ch, ok := d.Sync(); ok {
			columns = append(columns, ch)
			pCount++
		}
	}
	var aCount int
	for _, d := range s.aDecorators {
		if ch, ok := d.Sync(); ok {
			columns = append(columns, ch)
			aCount++
		}
	}
	table := make([][]chan int, 2)
	table[0] = columns[0:pCount]
	table[1] = columns[pCount : pCount+aCount : pCount+aCount]
	return table
}

func newStatistics(s *bState) *decor.Statistics {
	return &decor.Statistics{
		ID:        s.id,
		Completed: s.completeFlushed,
		Total:     s.total,
		Current:   s.current,
	}
}

func countLines(b []byte) int {
	return bytes.Count(b, []byte("\n"))
}
<|endoftext|>


<commit_before>package chainlib

import (
	"flag"
	"fmt"
	"os"
	"strings"
)

func assert(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

// Convenience function for writing utilities which use Bernstein chaining to
// pass environment variables. Function f should perform logic to retrieve data
// to be serialized to environment variables and return them as a map of strings,
// or error if something went wrong.
func Main(prefix string, f func() (map[string]string, error)) {
	var upcase bool
	var rprefix string
	flag.StringVar(&rprefix, "prefix", prefix, "Prefix")
	flag.BoolVar(&upcase, "upcase", true, "Upcase variables")
	flag.Parse()

	env, err := f()
	assert(err)
	for k, v := range env {
		key := SafeEnv(k)
		if upcase {
			key = strings.ToUpper(key)
		}
		if rprefix != "" {
			key = fmt.Sprintf("%s_%s", rprefix, key)
		}
		env[key] = v
	}
	assert(Exec(flag.Args(), Environment(env)))
}
<commit_msg>Fix duplication bug<commit_after>package chainlib

import (
	"flag"
	"fmt"
	"os"
	"strings"
)

func assert(err error) {
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

// Convenience function for writing utilities which use Bernstein chaining to
// pass environment variables. Function f should perform logic to retrieve data
// to be serialized to environment variables and return them as a map of strings,
// or error if something went wrong.
func Main(prefix string, f func() (map[string]string, error)) {
	var upcase bool
	var rprefix string
	flag.StringVar(&rprefix, "prefix", prefix, "Prefix")
	flag.BoolVar(&upcase, "upcase", true, "Upcase variables")
	flag.Parse()

	vars, err := f()
	assert(err)
	env := NewEnvironment()
	for k, v := range vars {
		key := SafeEnv(k)
		if upcase {
			key = strings.ToUpper(key)
		}
		if rprefix != "" {
			key = fmt.Sprintf("%s_%s", rprefix, key)
		}
		env[key] = v
	}
	assert(Exec(flag.Args(), env))
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"fmt"
)

var store = make(map[string]map[string]float64)

func InitBot(token string) (*tgbotapi.BotAPI, error) {
	bot, err := tgbotapi.NewBotAPI(token)
	if err != nil {
		return nil, err
	}

	log.Println("Account: ", bot.Self.UserName)

	return bot, nil
}

func InitWebHook(bot *tgbotapi.BotAPI, config Config) (tgbotapi.UpdatesChannel, error) {
	URL, err := url.Parse(config.Url)
	if err != nil {
		return nil, err
	}

	bot.SetWebhook(tgbotapi.WebhookConfig{URL: URL})

	updates := bot.ListenForWebhook(URL.Path)

	go http.ListenAndServe("localhost:"+config.Port, nil)
	if err != nil {
		return nil, err
	}

	return updates, nil
}

func ProcessUpdates(updates tgbotapi.UpdatesChannel, bot *tgbotapi.BotAPI) {

	for update := range updates {
		if update.Message == nil {
			continue
		}

		text := update.Message.Text
		parsedText := strings.Fields(text)
		var response string
		var money float64
		var err error
		var category string

		account := strconv.FormatInt(int64(update.Message.From.ID), 10)
		log.Println(parsedText, len(parsedText))
		if len(parsedText) >= 2 {
			money, err = strconv.ParseFloat(parsedText[1], 64)
			if err != nil {
				log.Println("[ERROR]: ", err)
			}
		} else {
			money = 0
		}

		if len(parsedText) >= 3 {
			category = parsedText[2]
		} else {
			category = "default"
		}
		log.Println(account, money, category)

		switch {
		case strings.Contains(text, "/start"):
			response = startMessage()
		case strings.HasPrefix(text, "+"):
			response = addMoney(money, account, category)
		case strings.HasPrefix(text, "-"):
			response = removeMoney(money, account, category)
		case strings.Contains(text, "/status"):
			response = getStatus(account)
		default:
			response = "i don't get what you want from me."
		}

		msg := tgbotapi.NewMessage(update.Message.Chat.ID, response)
		msg.ReplyToMessageID = update.Message.MessageID

		bot.Send(msg)
	}
}

func startMessage() string {
	return `This is check wallet bot, usage:
		   + <money> <category(optional)> - add money to wallet
		   - <money> <category(optional)> - remove money from wallet`
}

func addMoney(money float64, account, category string) string {
	_, ok := store[account]
	if !ok {
		store[account] = make(map[string]float64)
	}

	store[account][category] += money
	log.Println(store[account][category])
	return fmt.Sprintf(
		`add: %v
			category: %v`,
		strconv.FormatFloat(money, 'f', -1, 64),
		category)
}

func removeMoney(money float64, account, category string) string {
	_, ok := store[account]
	if !ok {
		store[account] = make(map[string]float64)
	}

	store[account][category] -= money
	log.Println(store[account][category])
	return fmt.Sprintf(
		`remove: %v
			category: %v`,
		strconv.FormatFloat(money, 'f', -1, 64),
		category)
}

func getStatus(account string) string {
	_, ok := store[account]
	if !ok {
		store[account] = make(map[string]float64)
	}
	var sum float64
	for _, val := range store[account] {
		sum += val
	}

	return "wallet: " + strconv.FormatFloat(sum, 'f', -1, 64)
}<commit_msg>add help message<commit_after>package main

import (
	"github.com/go-telegram-bot-api/telegram-bot-api"
	"log"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"fmt"
)

var store = make(map[string]map[string]float64)

func InitBot(token string) (*tgbotapi.BotAPI, error) {
	bot, err := tgbotapi.NewBotAPI(token)
	if err != nil {
		return nil, err
	}

	log.Println("Account: ", bot.Self.UserName)

	return bot, nil
}

func InitWebHook(bot *tgbotapi.BotAPI, config Config) (tgbotapi.UpdatesChannel, error) {
	URL, err := url.Parse(config.Url)
	if err != nil {
		return nil, err
	}

	bot.SetWebhook(tgbotapi.WebhookConfig{URL: URL})

	updates := bot.ListenForWebhook(URL.Path)

	go http.ListenAndServe("localhost:"+config.Port, nil)
	if err != nil {
		return nil, err
	}

	return updates, nil
}

func ProcessUpdates(updates tgbotapi.UpdatesChannel, bot *tgbotapi.BotAPI) {

	for update := range updates {
		if update.Message == nil {
			continue
		}

		text := update.Message.Text
		parsedText := strings.Fields(text)
		var response string
		var money float64
		var err error
		var category string

		account := strconv.FormatInt(int64(update.Message.From.ID), 10)
		log.Println(parsedText, len(parsedText))
		if len(parsedText) >= 2 {
			money, err = strconv.ParseFloat(parsedText[1], 64)
			if err != nil {
				log.Println("[ERROR]: ", err)
			}
		} else {
			money = 0
		}

		if len(parsedText) >= 3 {
			category = parsedText[2]
		} else {
			category = "default"
		}
		log.Println(account, money, category)

		switch {
		case strings.Contains(text, "/start") || strings.Contains(text, "/help"):
			response = startMessage()
		case strings.HasPrefix(text, "+"):
			response = addMoney(money, account, category)
		case strings.HasPrefix(text, "-"):
			response = removeMoney(money, account, category)
		case strings.Contains(text, "/status"):
			response = getStatus(account)
		default:
			response = "i don't get what you want from me."
		}

		msg := tgbotapi.NewMessage(update.Message.Chat.ID, response)
		msg.ReplyToMessageID = update.Message.MessageID

		bot.Send(msg)
	}
}

func startMessage() string {
	return `This is check wallet bot, usage:
		   + <money> <category(optional)> - add money to wallet
		   - <money> <category(optional)> - remove money from wallet
		   /status - to get your wallet status
		   /help - prints this message`
}

func addMoney(money float64, account, category string) string {
	_, ok := store[account]
	if !ok {
		store[account] = make(map[string]float64)
	}

	store[account][category] += money
	log.Println(store[account][category])
	return fmt.Sprintf(
		`add: %v
			category: %v`,
		strconv.FormatFloat(money, 'f', -1, 64),
		category)
}

func removeMoney(money float64, account, category string) string {
	_, ok := store[account]
	if !ok {
		store[account] = make(map[string]float64)
	}

	store[account][category] -= money
	log.Println(store[account][category])
	return fmt.Sprintf(
		`remove: %v
			category: %v`,
		strconv.FormatFloat(money, 'f', -1, 64),
		category)
}

func getStatus(account string) string {
	_, ok := store[account]
	if !ok {
		store[account] = make(map[string]float64)
	}
	var sum float64
	for _, val := range store[account] {
		sum += val
	}

	return "wallet: " + strconv.FormatFloat(sum, 'f', -1, 64)
}<|endoftext|>


<commit_before>package ircbot

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"net/textproto"
	"strings"
	"time"
)

type IrcBot struct {
	// identity
	User string
	Nick string

	// server info
	Server  string
	Port    string
	Channel []string

	// tcp communication
	conn   net.Conn
	reader *textproto.Reader
	writer *textproto.Writer

	// data flow
	In    chan *IrcMsg
	Out   chan *IrcMsg
	Error chan error

	// exit flag
	Exit chan bool

	//action handlers
	Handlers map[string]ActionFunc

	//are we joined in channel?
	joined bool
}

func NewIrcBot() *IrcBot {
	return &IrcBot{
		Handlers: make(map[string]ActionFunc),
		In:       make(chan *IrcMsg),
		Out:      make(chan *IrcMsg),
		Error:    make(chan error),
		Exit:     make(chan bool),
		joined:   false,
	}
}

func (b *IrcBot) url() string {
	return fmt.Sprintf("%s:%s", b.Server, b.Port)
}

func (b *IrcBot) Connect() {
	//launch a go routine that handle errors
	// b.handleError()

	log.Println("Info> connection to", b.url())
	tcpCon, err := net.Dial("tcp", b.url())
	if err != nil {
		log.Println("Error> ", err)
		b.Error <- err
	}

	b.conn = tcpCon
	r := bufio.NewReader(b.conn)
	w := bufio.NewWriter(b.conn)
	b.reader = textproto.NewReader(r)
	b.writer = textproto.NewWriter(w)

	b.writer.PrintfLine("USER %s 8 * :%s", b.Nick, b.Nick)
	b.writer.PrintfLine("NICK %s", b.Nick)
}

func (b *IrcBot) Join() {
	for _, v := range b.Channel {
		b.writer.PrintfLine("JOIN %s", v)
	}
	time.Sleep(2 * time.Second)
	b.joined = true
}

func (b *IrcBot) Listen() {

	go func() {

		for {
			//block read line from socket
			line, err := b.reader.ReadLine()
			if err != nil {
				b.Error <- err
			}
			//convert line into IrcMsg
			msg := Parseline(line)
			b.In <- msg
		}

	}()
}

func (b *IrcBot) Say(s string) {
	msg := NewIrcMsg()
	msg.command = "PRIVMSG"
	msg.args = append(msg.args, s)

	b.Out <- msg
}

func (b *IrcBot) HandleActionIn() {
	go func() {
		for {
			//receive new message
			msg := <-b.In
			fmt.Println("irc << ", msg.raw)
			//handle action
			if action := b.Handlers[msg.command]; action != nil {
				action(b, msg)
			}
		}
	}()
}

func (b *IrcBot) HandleActionOut() {
	go func() {
		for {
			msg := <-b.Out

			//we send nothing before we sure we join channel
			if b.joined == false {
				continue
			}

			s := fmt.Sprintf("%s %s", msg.command, strings.Join(msg.args, " "))
			fmt.Println("irc >> ", s)
			b.writer.PrintfLine(s)
		}
	}()
}

func (b *IrcBot) HandleError() {
	go func() {
		for {
			err := <-b.Error
			fmt.Printf("error > %s", err)
			if err != nil {
				b.Disconnect()
				log.Fatalln("Error ocurs :", err)
			}
		}
	}()
}

func (b *IrcBot) Disconnect() {
	b.writer.PrintfLine("QUIT")
	b.conn.Close()
}
<commit_msg>tls/ssl support<commit_after>package ircbot

import (
	"bufio"
	"crypto/rand"
	"crypto/tls"
	"fmt"
	"log"
	"net"
	"net/textproto"
	"strings"
	"time"
)

type IrcBot struct {
	// identity
	User string
	Nick string

	// server info
	Server  string
	Port    string
	Channel []string

	// tcp communication
	conn   net.Conn
	reader *textproto.Reader
	writer *textproto.Writer

	// crypto
	Encrypted bool
	config    tls.Config

	// data flow
	In    chan *IrcMsg
	Out   chan *IrcMsg
	Error chan error

	// exit flag
	Exit chan bool

	//action handlers
	Handlers map[string]ActionFunc

	//are we joined in channel?
	joined bool
}

func NewIrcBot() *IrcBot {
	return &IrcBot{
		Handlers: make(map[string]ActionFunc),
		In:       make(chan *IrcMsg),
		Out:      make(chan *IrcMsg),
		Error:    make(chan error),
		Exit:     make(chan bool),
		joined:   false,
	}
}

func (b *IrcBot) url() string {
	return fmt.Sprintf("%s:%s", b.Server, b.Port)
}

func (b *IrcBot) loadCert() {

}

func (b *IrcBot) Connect() {
	//launch a go routine that handle errors
	// b.handleError()

	log.Println("Info> connection to", b.url())

	var tcpCon net.Conn
	var err error
	if b.Encrypted {
		cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
		b.errChk(err)

		config := tls.Config{Certificates: []tls.Certificate{cert}}
		config.Rand = rand.Reader
		tcpCon, err = tls.Dial("tcp", b.url(), &config)
		b.errChk(err)

	} else {
		tcpCon, err = net.Dial("tcp", b.url())
		b.errChk(err)
	}

	b.conn = tcpCon
	r := bufio.NewReader(b.conn)
	w := bufio.NewWriter(b.conn)
	b.reader = textproto.NewReader(r)
	b.writer = textproto.NewWriter(w)

	b.writer.PrintfLine("USER %s 8 * :%s", b.Nick, b.Nick)
	b.writer.PrintfLine("NICK %s", b.Nick)
}

func (b *IrcBot) Join() {
	for _, v := range b.Channel {
		b.writer.PrintfLine("JOIN %s", v)
	}
	time.Sleep(2 * time.Second)
	b.joined = true
}

func (b *IrcBot) Listen() {

	go func() {

		for {
			//block read line from socket
			line, err := b.reader.ReadLine()
			if err != nil {
				b.Error <- err
			}
			//convert line into IrcMsg
			msg := Parseline(line)
			b.In <- msg
		}

	}()
}

func (b *IrcBot) Say(s string) {
	msg := NewIrcMsg()
	msg.command = "PRIVMSG"
	msg.args = append(msg.args, s)

	b.Out <- msg
}

func (b *IrcBot) HandleActionIn() {
	go func() {
		for {
			//receive new message
			msg := <-b.In
			fmt.Println("irc << ", msg.raw)
			//handle action
			if action := b.Handlers[msg.command]; action != nil {
				action(b, msg)
			}
		}
	}()
}

func (b *IrcBot) HandleActionOut() {
	go func() {
		for {
			msg := <-b.Out

			//we send nothing before we sure we join channel
			if b.joined == false {
				continue
			}

			s := fmt.Sprintf("%s %s", msg.command, strings.Join(msg.args, " "))
			fmt.Println("irc >> ", s)
			b.writer.PrintfLine(s)
		}
	}()
}

func (b *IrcBot) HandleError() {
	go func() {
		for {
			err := <-b.Error
			fmt.Printf("error > %s", err)
			if err != nil {
				b.Disconnect()
				log.Fatalln("Error ocurs :", err)
			}
		}
	}()
}

func (b *IrcBot) Disconnect() {
	b.writer.PrintfLine("QUIT")
	b.conn.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"sync"
	"time"
)

const (
	ExitCodeOK             int = 0
	ExitCodeError          int = 1
	ExitCodeFlagParseError     = 10 + iota
	ExitCodeFileOpenError
)

const HelpText string = `Usage: stdiolog [options] -- <command> [options]

Options:
  -h  Print this message and exit
  -o  The stdout log
  -e  The stderr log (defaults to -o)
`

var (
	outlog, errlog io.WriteCloser
	wg             sync.WaitGroup
)

type CLI struct {
	Piped          bool
	Stdin          io.Reader
	Stdout, Stderr io.Writer
}

// invoke the cli with args
func (cli *CLI) Run(args []string) int {
	var err error

	// parse args string
	flags := flag.NewFlagSet("cFlags", flag.ContinueOnError)
	flags.SetOutput(cli.Stdout)

	help := flags.Bool("h", false, "print help and quit")
	version := flags.Bool("v", false, "print version and exit")
	outfile := flags.String("o", "", "logfile for stdout")
	errfile := flags.String("e", "", "logfile for stderr")

	if err = flags.Parse(args[1:]); err != nil {
		return ExitCodeFlagParseError
	}

	if *version {
		fmt.Fprintf(cli.Stdout, "%s\n", Version)
		return ExitCodeOK
	}

	if *help {
		fmt.Fprintf(cli.Stderr, HelpText)
		return ExitCodeOK
	}

	childArgs := flags.Args()

	// open output files
	if outlog == nil {
		outlog, err = os.OpenFile(*outfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			fmt.Fprintf(cli.Stderr, err.Error())
			return ExitCodeFileOpenError
		}
		defer outlog.Close()
	}

	if *errfile == "" {
		errlog = outlog
	} else {
		errlog, err = os.OpenFile(*errfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			fmt.Fprintf(cli.Stderr, err.Error())
			return ExitCodeFileOpenError
		}
		defer errlog.Close()
	}

	// setup child process
	cmd := exec.Command(childArgs[0], childArgs[1:]...)
	cmdout, _ := cmd.StdoutPipe()
	cmderr, _ := cmd.StderrPipe()

	// check for incoming data on stdin
	if cli.Piped {
		wg.Add(1)
		stdin, _ := cmd.StdinPipe()
		go func() {
			// copy stdin to child process then close stdin
			if _, err := io.Copy(stdin, cli.Stdin); err == nil {
				stdin.Close()
				wg.Done()
			} else {
				panic(err)
			}
		}()
	}

	wg.Add(2)
	go ReadWriteLine(cmdout, &Logger{cli.Stdout, outlog})
	go ReadWriteLine(cmderr, &Logger{cli.Stderr, errlog})

	cmd.Start()
	wg.Wait()

	return ExitCodeOK
}

type Logger struct {
	Stdio   io.Writer
	Logfile io.WriteCloser
}

func (l *Logger) Write(p []byte) (n int, err error) {
	now := time.Now().Format(time.RFC3339)
	// don't block writing to stdio
	go fmt.Fprintf(l.Logfile, "%s %s", now, p)
	return fmt.Fprintf(l.Stdio, "%s", p)
}

func (l *Logger) Close() error {
	return l.Logfile.Close()
}

func ReadWriteLine(reader io.ReadCloser, writer io.Writer) {
	s := bufio.NewReader(reader)
	for {
		if l, _, err := s.ReadLine(); err == io.EOF {
			if len(l) > 0 {
				writer.Write(l)
			}
			break
		} else if err != nil {
			panic(err)
		} else {
			writer.Write(append(l, 0x0A))
		}
	}
	wg.Done()
}
<commit_msg>change readwriteline method sig<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"sync"
	"time"
)

const (
	ExitCodeOK             int = 0
	ExitCodeError          int = 1
	ExitCodeFlagParseError     = 10 + iota
	ExitCodeFileOpenError
)

const HelpText string = `Usage: stdiolog [options] -- <command> [options]

Options:
  -h  Print this message and exit
  -o  The stdout log
  -e  The stderr log (defaults to -o)
`

var (
	outlog, errlog io.WriteCloser
	wg             sync.WaitGroup
)

type CLI struct {
	Piped          bool
	Stdin          io.Reader
	Stdout, Stderr io.Writer
}

// invoke the cli with args
func (cli *CLI) Run(args []string) int {
	var err error

	// parse args string
	flags := flag.NewFlagSet("cFlags", flag.ContinueOnError)
	flags.SetOutput(cli.Stdout)

	help := flags.Bool("h", false, "print help and quit")
	version := flags.Bool("v", false, "print version and exit")
	outfile := flags.String("o", "", "logfile for stdout")
	errfile := flags.String("e", "", "logfile for stderr")

	if err = flags.Parse(args[1:]); err != nil {
		return ExitCodeFlagParseError
	}

	if *version {
		fmt.Fprintf(cli.Stdout, "%s\n", Version)
		return ExitCodeOK
	}

	if *help {
		fmt.Fprintf(cli.Stderr, HelpText)
		return ExitCodeOK
	}

	childArgs := flags.Args()

	// open output files
	if outlog == nil {
		outlog, err = os.OpenFile(*outfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			fmt.Fprintf(cli.Stderr, err.Error())
			return ExitCodeFileOpenError
		}
		defer outlog.Close()
	}

	if *errfile == "" {
		errlog = outlog
	} else {
		errlog, err = os.OpenFile(*errfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			fmt.Fprintf(cli.Stderr, err.Error())
			return ExitCodeFileOpenError
		}
		defer errlog.Close()
	}

	// setup child process
	cmd := exec.Command(childArgs[0], childArgs[1:]...)
	cmdout, _ := cmd.StdoutPipe()
	cmderr, _ := cmd.StderrPipe()

	// check for incoming data on stdin
	if cli.Piped {
		wg.Add(1)
		stdin, _ := cmd.StdinPipe()
		go func() {
			// copy stdin to child process then close stdin
			if _, err := io.Copy(stdin, cli.Stdin); err == nil {
				stdin.Close()
				wg.Done()
			} else {
				panic(err)
			}
		}()
	}

	wg.Add(2)
	go ReadWriteLine(cmdout, &Logger{cli.Stdout, outlog})
	go ReadWriteLine(cmderr, &Logger{cli.Stderr, errlog})

	cmd.Start()
	wg.Wait()

	return ExitCodeOK
}

type Logger struct {
	Stdio   io.Writer
	Logfile io.WriteCloser
}

func (l *Logger) Write(p []byte) (n int, err error) {
	now := time.Now().Format(time.RFC3339)
	// don't block writing to stdio
	go fmt.Fprintf(l.Logfile, "%s %s", now, p)
	return fmt.Fprintf(l.Stdio, "%s", p)
}

func (l *Logger) Close() error {
	return l.Logfile.Close()
}

func ReadWriteLine(reader io.Reader, writer io.Writer) {
	s := bufio.NewReader(reader)
	for {
		if l, _, err := s.ReadLine(); err == io.EOF {
			if len(l) > 0 {
				writer.Write(l)
			}
			break
		} else if err != nil {
			panic(err)
		} else {
			writer.Write(append(l, 0x0A))
		}
	}
	wg.Done()
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 by Pierre Thirouin. All rights reserved.

// This file is part of dotfiles, a simple dotfiles manager.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
)

var (
	debugMode = false
	quietMode = false
)

var help = `usage: dotfiles [command]

The commands are:
  create    Create a new .dotfiles directory
  init      Initialize your config difine in the .dotfiles dir
`

var (
	// RootDir is the directory where files will be link or copy.
	RootDir = "."

	// DotFilesDir is the name of the directory where the dotfiles are stored.
	DotFilesDir = ".dotfiles"

	// BaseDir is the path to the dotfiles directory
	BaseDir = filepath.Join(RootDir, DotFilesDir)

	dirs = [8]string{"bin", "conf", "copy", "init", "link", "source", "test", "vendor"}
)

func changeRootDir(path string) {
	RootDir = path
	BaseDir = filepath.Join(RootDir, DotFilesDir)
}

func main() {
	flag.Parse()

	command := flag.Arg(0)

	usr, err := user.Current()
	if err != nil {
		fmt.Errorf("%v", err)
	}

	changeRootDir(usr.HomeDir)

	if command == "create" {
		initialize()
	} else if command == "init" {
		printHeader("Copying files into home directory")
		copyDir()

		printHeader("Linking files into home directory")
		linkDir()

		printHeader("Run the following init scripts")
		initDir()

		sourceDir()

		printHeader("All done !")
	} else {
		fmt.Print(help)
	}
}

func printHeader(s string) {
	if !quietMode {
		fmt.Printf("\033[1m%s\033[0m\n", s)
	}
}

func printArrow(s string) {
	if !quietMode {
		fmt.Printf(" \033[1;34m➜\033[0m  %s\n", s)
	}
}

// Initialize creates a new .dotfiles repo
func initialize() {
	printHeader("Scaffold " + BaseDir)

	for _, dir := range dirs {
		printArrow(dir)
		os.MkdirAll(filepath.Join(BaseDir, dir), 0777)
	}
	if !quietMode {
		fmt.Println("")
	}
	printHeader("All done !")
}

// CloneRepo clones the given git repository
func cloneRepo(gitrepo string) {
	printHeader("Clone " + gitrepo)
	git, err := exec.LookPath("git")
	if err != nil {
		fmt.Errorf("git is required to clone the dotfiles repo")
	}

	err = exec.Command(git, "clone", gitrepo, BaseDir).Run()
	if err != nil {
		fmt.Errorf("Failed to clone %s", gitrepo)
	}

	printHeader(BaseDir + " is ready !")
}

func backup(file string) {
	// If there is no backup dir yet create it
	if _, err := os.Stat(filepath.Join(BaseDir, "backup")); os.IsNotExist(err) {
		os.Mkdir(filepath.Join(BaseDir, "backup"), 0777)
	}

	if _, err := os.Stat(filepath.Join(RootDir, file)); !os.IsNotExist(err) {
		// The file already exists so backup it
		printHeader("Backup %s")

		err = exec.Command("mv", filepath.Join(RootDir, file), filepath.Join(BaseDir, "backup", file)).Run()
		if err != nil {
			fmt.Errorf("Failed to backup %s\n%v", file, err)
		}
	}
}

// CopyDir copies all the files in the copy dir at ~/
func copyDir() {
	applyCmd("copy", func(fileToCopy string) *exec.Cmd {
		return exec.Command("cp", fileToCopy, RootDir)
	})
}

// LinkDir links all the files in the link dir at ~/
func linkDir() {
	applyCmd("link", func(fileToLink string) *exec.Cmd {
		return exec.Command("ln", "-sf", fileToLink, RootDir)
	})
}

// InitDir executes all the scripts in the init dir
func initDir() []byte {
	out := applyCmd("init", func(initFile string) *exec.Cmd {
		return exec.Command("/bin/bash", initFile)
	})
	if len(out) != 0 {
		fmt.Printf("%s", string(out))
	}
	return out
}

// SourceDir source all the files in the source dir
// Solution from here: http://stackoverflow.com/a/29995987/1292605
func sourceDir() {
	dir := "source"
	dirPath := filepath.Join(BaseDir, dir)

	files, readErr := ioutil.ReadDir(dirPath)
	if readErr != nil {
		fmt.Errorf("Failed to read %s dir at %s", dir, dirPath)
	}

	for _, file := range files {
		printHeader("Sourcing " + file.Name())
		fileToSource := filepath.Join(dirPath, file.Name())

		cmd := exec.Command("/bin/bash", "-c", "source "+fileToSource+" ; echo '<<<ENVIRONMENT>>>' ; env")
		out, err := cmd.CombinedOutput()
		if err != nil {
			fmt.Errorf("Failed to source %s", file.Name())
		}

		s := bufio.NewScanner(bytes.NewReader(out))
		start := false
		for s.Scan() {
			if s.Text() == "<<<ENVIRONMENT>>>" {
				start = true
			} else if start {
				kv := strings.SplitN(s.Text(), "=", 2)
				if len(kv) == 2 {
					os.Setenv(kv[0], kv[1])
				}
			}
		}

	}
}

func applyCmd(dir string, cmdFactory func(string) *exec.Cmd) []byte {
	dirPath := filepath.Join(BaseDir, dir)

	files, readErr := ioutil.ReadDir(dirPath)
	if readErr != nil {
		fmt.Errorf("Failed to read %s dir at %s", dir, dirPath)
	}

	output := []byte{}

	for _, file := range files {
		fileToCopy := filepath.Join(dirPath, file.Name())

		cmd := cmdFactory(fileToCopy)
		if debugMode {
			fmt.Printf("=> Executing: %s\n", strings.Join(cmd.Args, " "))
		}

		out, err := cmd.CombinedOutput()
		if err != nil {
			fmt.Errorf("Failed to %s file: %s", dir, fileToCopy)
		}
		if debugMode {
			fmt.Printf("%s\n", string(out))
		}
		output = append(output, out...)

	}

	return output
}
<commit_msg>Refactor the way commands are applied<commit_after>// Copyright (c) 2015 by Pierre Thirouin. All rights reserved.

// This file is part of dotfiles, a simple dotfiles manager.

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
)

var (
	debugMode = false
	quietMode = false
)

var help = `usage: dotfiles [command]

The commands are:
  create    Create a new .dotfiles directory
  init      Initialize your config difine in the .dotfiles dir
`

var (
	// RootDir is the directory where files will be link or copy.
	RootDir = "."

	// DotFilesDir is the name of the directory where the dotfiles are stored.
	DotFilesDir = ".dotfiles"

	// BaseDir is the path to the dotfiles directory
	BaseDir = filepath.Join(RootDir, DotFilesDir)

	dirs = [8]string{"bin", "conf", "copy", "init", "link", "source", "test", "vendor"}
)

func changeRootDir(path string) {
	RootDir = path
	BaseDir = filepath.Join(RootDir, DotFilesDir)
}

func main() {
	flag.Parse()

	command := flag.Arg(0)

	usr, err := user.Current()
	if err != nil {
		fmt.Errorf("%v", err)
	}

	changeRootDir(usr.HomeDir)

	if command == "create" {
		initialize()
	} else if command == "init" {
		printHeader("Copying files into home directory")
		copyDir()

		printHeader("Linking files into home directory")
		linkDir()

		printHeader("Run the following init scripts")
		initDir()

		sourceDir()

		printHeader("All done !")
	} else {
		fmt.Print(help)
	}
}

func printHeader(s string) {
	if !quietMode {
		fmt.Printf("\033[1m%s\033[0m\n", s)
	}
}

func printArrow(s string) {
	if !quietMode {
		fmt.Printf(" \033[1;34m➜\033[0m  %s\n", s)
	}
}

// Initialize creates a new .dotfiles repo
func initialize() {
	printHeader("Scaffold " + BaseDir)

	for _, dir := range dirs {
		printArrow(dir)
		os.MkdirAll(filepath.Join(BaseDir, dir), 0777)
	}
	if !quietMode {
		fmt.Println("")
	}
	printHeader("All done !")
}

// CloneRepo clones the given git repository
func cloneRepo(gitrepo string) {
	printHeader("Clone " + gitrepo)
	git, err := exec.LookPath("git")
	if err != nil {
		fmt.Errorf("git is required to clone the dotfiles repo")
	}

	err = exec.Command(git, "clone", gitrepo, BaseDir).Run()
	if err != nil {
		fmt.Errorf("Failed to clone %s", gitrepo)
	}

	printHeader(BaseDir + " is ready !")
}

func backup(file string) {
	// If there is no backup dir yet create it
	if _, err := os.Stat(filepath.Join(BaseDir, "backup")); os.IsNotExist(err) {
		os.Mkdir(filepath.Join(BaseDir, "backup"), 0777)
	}

	if _, err := os.Stat(filepath.Join(RootDir, file)); !os.IsNotExist(err) {
		// The file already exists so backup it
		printHeader("Backup %s")

		err = exec.Command("mv", filepath.Join(RootDir, file), filepath.Join(BaseDir, "backup", file)).Run()
		if err != nil {
			fmt.Errorf("Failed to backup %s\n%v", file, err)
		}
	}
}

// CopyDir copies all the files in the copy dir at ~/
func copyDir() {
	applyCmd("copy", func(fileToCopy string) error {
		return exec.Command("cp", fileToCopy, RootDir).Run()
	})
}

// LinkDir links all the files in the link dir at ~/
func linkDir() {
	applyCmd("link", func(file string) error {
		return exec.Command("ln", "-sf", file, RootDir).Run()
	})
}

// InitDir executes all the scripts in the init dir
func initDir() []byte {
	var out []byte

	applyCmd("init", func(initFile string) error {
		output, err := exec.Command("/bin/bash", initFile).CombinedOutput()
		out = append(out, output...)
		return err
	})

	if !quietMode && len(out) != 0 {
		fmt.Printf("%s", string(out))
	}
	return out
}

// SourceDir source all the files in the source dir
// Solution from here: http://stackoverflow.com/a/29995987/1292605
func sourceDir() {
	applyCmd("source", func(file string) error {
		printHeader("Sourcing " + file)

		cmd := exec.Command("/bin/bash", "-c", "source "+file+" ; echo '<<<ENVIRONMENT>>>' ; env")
		out, err := cmd.CombinedOutput()
		if err != nil {
			return err
		}

		s := bufio.NewScanner(bytes.NewReader(out))
		start := false
		for s.Scan() {
			if s.Text() == "<<<ENVIRONMENT>>>" {
				start = true
			} else if start {
				kv := strings.SplitN(s.Text(), "=", 2)
				if len(kv) == 2 {
					os.Setenv(kv[0], kv[1])
				}
			}
		}

		return nil
	})
}

func applyCmd(dir string, execCmd func(string) error) {
	dirPath := filepath.Join(BaseDir, dir)

	files, readErr := ioutil.ReadDir(dirPath)
	if readErr != nil {
		fmt.Errorf("Failed to read %s dir at %s", dir, dirPath)
	}

	for _, file := range files {
		err := execCmd(filepath.Join(dirPath, file.Name()))
		if err != nil {
			fmt.Errorf("Failed to %s file: %s", dir, file.Name())
		}
	}
}
<|endoftext|>


<commit_before>package bot

import (
	"fmt"
	"log"
	"sync"
)

// Cmd holds the parsed user's input for easier handling of commands
type Cmd struct {
	Raw     string   // Raw is full string passed to the command
	Channel string   // Channel where the command was called
	User	*User    // User who sent the message
	Message string   // Full string without the prefix
	Command string   // Command is the first argument passed to the bot
	RawArgs string   // Raw arguments after the command
	Args    []string // Arguments as array
}

// PassiveCmd holds the information which will be passed to passive commands when receiving a message
type PassiveCmd struct {
	Raw     string // Raw message sent to the channel
	Channel string // Channel which the message was sent to
	User    *User  // User who sent this message
}

// User holds user id (nick) and real name
type User struct {
	Nick		string
	RealName	string
}

type customCommand struct {
	Version     int
	Cmd         string
	CmdFuncV1   activeCmdFuncV1
	CmdFuncV2   activeCmdFuncV2
	Description string
	ExampleArgs string
}

type incomingMessage struct {
	Channel			string
	Text			string
	User			*User
	BotCurrentNick	string
}

// CmdResult is the result message of V2 commands
type CmdResult struct {
	Channel string // The channel where the bot should send the message
	Message string // The message to be sent
}

const (
	v1 = iota
	v2
)

const (
	commandNotAvailable   = "Command %v not available."
	noCommandsAvailable   = "No commands available."
	errorExecutingCommand = "Error executing %s: %s"
)

type passiveCmdFunc func(cmd *PassiveCmd) (string, error)
type activeCmdFuncV1 func(cmd *Cmd) (string, error)
type activeCmdFuncV2 func(cmd *Cmd) (CmdResult, error)

var (
	commands        = make(map[string]*customCommand)
	passiveCommands = make(map[string]passiveCmdFunc)
)

// RegisterCommand adds a new command to the bot.
// The command(s) should be registered in the Init() func of your package
// command: String which the user will use to execute the command, example: reverse
// decription: Description of the command to use in !help, example: Reverses a string
// exampleArgs: Example args to be displayed in !help <command>, example: string to be reversed
// cmdFunc: Function which will be executed. It will received a parsed command as a Cmd value
func RegisterCommand(command, description, exampleArgs string, cmdFunc activeCmdFuncV1) {
	commands[command] = &customCommand{
		Version:     v1,
		Cmd:         command,
		CmdFuncV1:   cmdFunc,
		Description: description,
		ExampleArgs: exampleArgs,
	}
}

// RegisterCommandV2 adds a new command to the bot.
// It is the same as RegisterCommand but the command can specify the channel to reply to
func RegisterCommandV2(command, description, exampleArgs string, cmdFunc activeCmdFuncV2) {
	commands[command] = &customCommand{
		Version:     v2,
		Cmd:         command,
		CmdFuncV2:   cmdFunc,
		Description: description,
		ExampleArgs: exampleArgs,
	}
}

// RegisterPassiveCommand adds a new passive command to the bot.
// The command should be registered in the Init() func of your package
// Passive commands receives all the text posted to a channel without any parsing
// command: String used to identify the command, for internal use only (ex: logs)
// cmdFunc: Function which will be executed. It will received the raw message, channel and nick
func RegisterPassiveCommand(command string, cmdFunc func(cmd *PassiveCmd) (string, error)) {
	passiveCommands[command] = cmdFunc
}

func executePassiveCommands(cmd *PassiveCmd) {
	var wg sync.WaitGroup
	mutex := &sync.Mutex{}

	for _, v := range passiveCommands {
		cmdFunc := v

		wg.Add(1)

		go func() {
			defer wg.Done()

			result, err := cmdFunc(cmd)
			if err != nil {
				log.Println(err)
			} else {
				mutex.Lock()
				handlers.Response(cmd.Channel, result, cmd.User)
				mutex.Unlock()
			}
		}()
	}

	wg.Wait()
}

func handleCmd(c *Cmd) {
	cmd := commands[c.Command]

	if cmd == nil {
		log.Printf("Command not found %v", c.Command)
		return
	}

	switch cmd.Version {
	case v1:
		message, err := cmd.CmdFuncV1(c)
		checkCmdError(err, c)
		if message != "" {
			handlers.Response(c.Channel, message, c.User)
		}
	case v2:
		result, err := cmd.CmdFuncV2(c)
		checkCmdError(err, c)
		if result.Channel == "" {
			result.Channel = c.Channel
		}

		if result.Message != "" {
			handlers.Response(result.Channel, result.Message, c.User)
		}
	}
}

func checkCmdError(err error, c *Cmd) {
	if err != nil {
		errorMsg := fmt.Sprintf(errorExecutingCommand, c.Command, err.Error())
		log.Printf(errorMsg)
		handlers.Response(c.Channel, errorMsg, c.User)
	}
}
<commit_msg>Adds function scheduler based on cron tab specification<commit_after>package bot

import (
	"fmt"
	"log"
	"sync"
	"github.com/robfig/cron"
)

// Cmd holds the parsed user's input for easier handling of commands
type Cmd struct {
	Raw     string   // Raw is full string passed to the command
	Channel string   // Channel where the command was called
	User	*User    // User who sent the message
	Message string   // Full string without the prefix
	Command string   // Command is the first argument passed to the bot
	RawArgs string   // Raw arguments after the command
	Args    []string // Arguments as array
}

// PassiveCmd holds the information which will be passed to passive commands when receiving a message
type PassiveCmd struct {
	Raw     string	// Raw message sent to the channel
	Channel string	// Channel which the message was sent to
	User    *User	// User who sent this message
}

// PeriodicConfig holds a cron specification for periodically notifying the configured channels
type PeriodicConfig struct {
	CronSpec string		// CronSpec that schedules some function
	Channels []string	// A list of channels to notify
}

// User holds user id (nick) and real name
type User struct {
	Nick		string
	RealName	string
}

type customCommand struct {
	Version     int
	Cmd         string
	CmdFuncV1   activeCmdFuncV1
	CmdFuncV2   activeCmdFuncV2
	Description string
	ExampleArgs string
}

type incomingMessage struct {
	Channel			string
	Text			string
	User			*User
	BotCurrentNick	string
}

// CmdResult is the result message of V2 commands
type CmdResult struct {
	Channel string // The channel where the bot should send the message
	Message string // The message to be sent
}

const (
	v1 = iota
	v2
)

const (
	commandNotAvailable   = "Command %v not available."
	noCommandsAvailable   = "No commands available."
	errorExecutingCommand = "Error executing %s: %s"
)

type passiveCmdFunc func(cmd *PassiveCmd) (string, error)
type activeCmdFuncV1 func(cmd *Cmd) (string, error)
type activeCmdFuncV2 func(cmd *Cmd) (CmdResult, error)

var (
	commands        = make(map[string]*customCommand)
	passiveCommands = make(map[string]passiveCmdFunc)
	c = cron.New()
)

// RegisterCommand adds a new command to the bot.
// The command(s) should be registered in the Init() func of your package
// command: String which the user will use to execute the command, example: reverse
// decription: Description of the command to use in !help, example: Reverses a string
// exampleArgs: Example args to be displayed in !help <command>, example: string to be reversed
// cmdFunc: Function which will be executed. It will received a parsed command as a Cmd value
func RegisterCommand(command, description, exampleArgs string, cmdFunc activeCmdFuncV1) {
	commands[command] = &customCommand{
		Version:     v1,
		Cmd:         command,
		CmdFuncV1:   cmdFunc,
		Description: description,
		ExampleArgs: exampleArgs,
	}
}

// RegisterCommandV2 adds a new command to the bot.
// It is the same as RegisterCommand but the command can specify the channel to reply to
func RegisterCommandV2(command, description, exampleArgs string, cmdFunc activeCmdFuncV2) {
	commands[command] = &customCommand{
		Version:     v2,
		Cmd:         command,
		CmdFuncV2:   cmdFunc,
		Description: description,
		ExampleArgs: exampleArgs,
	}
}

// RegisterPassiveCommand adds a new passive command to the bot.
// The command should be registered in the Init() func of your package
// Passive commands receives all the text posted to a channel without any parsing
// command: String used to identify the command, for internal use only (ex: logs)
// cmdFunc: Function which will be executed. It will received the raw message, channel and nick
func RegisterPassiveCommand(command string, cmdFunc func(cmd *PassiveCmd) (string, error)) {
	passiveCommands[command] = cmdFunc
}

// RegisterPeriodicCommand adds a command that is run periodically.
// The command should be registered in the Init() func of your package
// config: PeriodicConfig which specify CronSpec and a channel list
// cmdFunc: A no-arg function which gets triggered periodically
func RegisterPeriodicCommand(config *PeriodicConfig, cmdFunc func() (string, error)) {
	c.AddFunc(config.CronSpec, func() {
		message, err := cmdFunc()
		if err != nil {
			log.Print("Periodic command failed ", err)
			return
		}
		if message != "" {
			for _, channel := range(config.Channels) {
				handlers.Response(channel, message, nil)
			}
		}
	})
	if len(c.Entries()) == 1 {
		c.Start()
	}
}

func executePassiveCommands(cmd *PassiveCmd) {
	var wg sync.WaitGroup
	mutex := &sync.Mutex{}

	for _, v := range passiveCommands {
		cmdFunc := v

		wg.Add(1)

		go func() {
			defer wg.Done()

			result, err := cmdFunc(cmd)
			if err != nil {
				log.Println(err)
			} else {
				mutex.Lock()
				handlers.Response(cmd.Channel, result, cmd.User)
				mutex.Unlock()
			}
		}()
	}

	wg.Wait()
}

func handleCmd(c *Cmd) {
	cmd := commands[c.Command]

	if cmd == nil {
		log.Printf("Command not found %v", c.Command)
		return
	}

	switch cmd.Version {
	case v1:
		message, err := cmd.CmdFuncV1(c)
		checkCmdError(err, c)
		if message != "" {
			handlers.Response(c.Channel, message, c.User)
		}
	case v2:
		result, err := cmd.CmdFuncV2(c)
		checkCmdError(err, c)
		if result.Channel == "" {
			result.Channel = c.Channel
		}

		if result.Message != "" {
			handlers.Response(result.Channel, result.Message, c.User)
		}
	}
}

func checkCmdError(err error, c *Cmd) {
	if err != nil {
		errorMsg := fmt.Sprintf(errorExecutingCommand, c.Command, err.Error())
		log.Printf(errorMsg)
		handlers.Response(c.Channel, errorMsg, c.User)
	}
}
<|endoftext|>