<commit_before>/*
Package goriot is a library that provides a means of connecting and recieving data from Riot's League of Legend API
*/
package goriot

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"
)

var (
	apikey string
	//BaseURL is the base of the url used by Riot's API service
	BaseURL = "api.pvp.net"
	//BaseURL is the base of the url used by Riot's API service
	BaseAPIURL = BaseURL + "/api"
	//BaseURL is the base of the url used by Riot's API service
	BaseObserverURL = BaseURL + "/observer-mode/rest"
	//BR represents the string for the Brazilian League of Legends Servers,
	//only used as a helper to prevent typos
	BR = "br"
	//EUNE represents the string for the North Eastern European League of Legends Servers,
	//only used as a helper to prevent typos
	EUNE = "eune"
	//EUW represents the string for the West European League of Legends Servers,
	//only used as a helper to prevent typos
	EUW = "euw"
	//KR represents the string for the Korean League of Legends Servers,
	//only used as a helper to prevent typos
	KR = "kr"
	//LAN represents the string for the Latin America North League of Legends Servers,
	//only used as a helper to prevent typos
	LAN = "lan"
	//LAS represents the string for the Latin America South League of Legends Servers,
	//only used as a helper to prevent typos
	LAS = "las"
	//NA represents the string for the North American League of Legends Servers,
	//only used as a helper to prevent typos
	NA = "na"
	//OCE represents the string for the Oceanic League of Legends Servers,
	//only used as a helper to prevent typos
	OCE = "oce"
	//RU represents the string for the Russian League of Legends Servers,
	//only used as a helper to prevent typos
	RU = "ru"
	//TR represents the string for the Turkish League of Legends Servers,
	//only used as a helper to prevent typos
	TR = "tr"
	//SEASON3 is the string of "SEASON3". Used to help prevent typos
	SEASON3 = "SEASON3"
	//SEASON4 is the string of "SEASON4". Used to help prevent typos
	SEASON4 = "SEASON4"
	//Ranked Solo 5s
	RANKED_SOLO_5x5 = "RANKED_SOLO_5x5"
	//Ranked Team 3s
	RANKED_TEAM_3x3 = "RANKED_TEAM_3x3"
	//Ranked Team 5s
	RANKED_TEAM_5x5 = "RANKED_TEAM_5x5"
	//ErrAPIKeyNotSet is the error returned when no global API key has been set
	ErrAPIKeyNotSet = errors.New("goriot: API key has not been set. If you need a key visit https://developer.riotgames.com/")
	smallRateChan   rateChan
	longRateChan    rateChan
)

type rateChan struct {
	RateQueue   chan bool
	TriggerChan chan bool
}

//RiotError contains the http status code of the erro
type RiotError struct {
	StatusCode int
}

// types for URL query parameter formatting
type strURLParameter []string
type intURLParameter []int64

// Creates a comma separated string
func (q strURLParameter) String() string {
	return strings.Join(q, ",")
}

// Convers int64 slice to string slice and returns
// comma separated string
func (q intURLParameter) String() string {
	s := make(strURLParameter, len(q))
	for k, v := range q {
		s[k] = strconv.FormatInt(v, 10)
	}

	return s.String()
}

//SetAPIKey sets the global key for the Riot Games API. If you do not have one you can get one for free at
//https://developer.riotgames.com/
func SetAPIKey(key string) {
	apikey = key
}

//SetSmallRateLimit allows a custom rate limit to be set. For at the time of this writing the default
//for a development API key is 10 requests every 10 seconds
func SetSmallRateLimit(numrequests int, pertime time.Duration) {
	smallRateChan = rateChan{
		RateQueue:   make(chan bool, numrequests),
		TriggerChan: make(chan bool),
	}
	go rateLimitHandler(smallRateChan, pertime)
}

//SetLongRateLimit allows a custom rate limit to be set. For at the time of this writing the default
//for a development API key is 500 requests every 10 minutes
func SetLongRateLimit(numrequests int, pertime time.Duration) {
	longRateChan = rateChan{
		RateQueue:   make(chan bool, numrequests),
		TriggerChan: make(chan bool),
	}
	go rateLimitHandler(longRateChan, pertime)
}

func rateLimitHandler(RateChan rateChan, pertime time.Duration) {
	returnChan := make(chan bool)
	go timeTriggerWatcher(RateChan.TriggerChan, returnChan)
	for {
		<-returnChan
		<-time.After(pertime)
		go timeTriggerWatcher(RateChan.TriggerChan, returnChan)
		length := len(RateChan.RateQueue)
		for i := 0; i < length; i++ {
			<-RateChan.RateQueue
		}
	}
}

func timeTriggerWatcher(timeTrigger chan bool, returnChan chan bool) {
	timeTrigger <- true
	returnChan <- true
}

//IsKeySet returns a bool of if the global API key has been set
func IsKeySet() bool {
	return !(apikey == "")
}

func requestAndUnmarshal(requestURL string, v interface{}) (err error) {
	checkRateLimiter(smallRateChan)
	checkRateLimiter(longRateChan)
	resp, err := http.Get(requestURL)
	defer resp.Body.Close()
	if err != nil {
		return
	}
	checkTimeTrigger(smallRateChan)
	checkTimeTrigger(longRateChan)
	if resp.StatusCode != http.StatusOK {
		return RiotError{StatusCode: resp.StatusCode}
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(body, v)
	if err != nil {
		return
	}
	return
}

func checkRateLimiter(RateChan rateChan) {
	if RateChan.RateQueue != nil && RateChan.TriggerChan != nil {
		RateChan.RateQueue <- true
	}
}

func checkTimeTrigger(RateChan rateChan) {
	if RateChan.RateQueue != nil && RateChan.TriggerChan != nil {
		select {
		case <-RateChan.TriggerChan:
		default:
		}
	}
}

//Error prints the error message for a RiotError
func (err RiotError) Error() string {
	return fmt.Sprintf("Error: HTTP Status %d", err.StatusCode)
}
<commit_msg>Closing of response body _after_ checking for error in requestAndUnmarshal See http://stackoverflow.com/a/16280362<commit_after>/*
Package goriot is a library that provides a means of connecting and recieving data from Riot's League of Legend API
*/
package goriot

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"time"
)

var (
	apikey string
	//BaseURL is the base of the url used by Riot's API service
	BaseURL = "api.pvp.net"
	//BaseURL is the base of the url used by Riot's API service
	BaseAPIURL = BaseURL + "/api"
	//BaseURL is the base of the url used by Riot's API service
	BaseObserverURL = BaseURL + "/observer-mode/rest"
	//BR represents the string for the Brazilian League of Legends Servers,
	//only used as a helper to prevent typos
	BR = "br"
	//EUNE represents the string for the North Eastern European League of Legends Servers,
	//only used as a helper to prevent typos
	EUNE = "eune"
	//EUW represents the string for the West European League of Legends Servers,
	//only used as a helper to prevent typos
	EUW = "euw"
	//KR represents the string for the Korean League of Legends Servers,
	//only used as a helper to prevent typos
	KR = "kr"
	//LAN represents the string for the Latin America North League of Legends Servers,
	//only used as a helper to prevent typos
	LAN = "lan"
	//LAS represents the string for the Latin America South League of Legends Servers,
	//only used as a helper to prevent typos
	LAS = "las"
	//NA represents the string for the North American League of Legends Servers,
	//only used as a helper to prevent typos
	NA = "na"
	//OCE represents the string for the Oceanic League of Legends Servers,
	//only used as a helper to prevent typos
	OCE = "oce"
	//RU represents the string for the Russian League of Legends Servers,
	//only used as a helper to prevent typos
	RU = "ru"
	//TR represents the string for the Turkish League of Legends Servers,
	//only used as a helper to prevent typos
	TR = "tr"
	//SEASON3 is the string of "SEASON3". Used to help prevent typos
	SEASON3 = "SEASON3"
	//SEASON4 is the string of "SEASON4". Used to help prevent typos
	SEASON4 = "SEASON4"
	//Ranked Solo 5s
	RANKED_SOLO_5x5 = "RANKED_SOLO_5x5"
	//Ranked Team 3s
	RANKED_TEAM_3x3 = "RANKED_TEAM_3x3"
	//Ranked Team 5s
	RANKED_TEAM_5x5 = "RANKED_TEAM_5x5"
	//ErrAPIKeyNotSet is the error returned when no global API key has been set
	ErrAPIKeyNotSet = errors.New("goriot: API key has not been set. If you need a key visit https://developer.riotgames.com/")
	smallRateChan   rateChan
	longRateChan    rateChan
)

type rateChan struct {
	RateQueue   chan bool
	TriggerChan chan bool
}

//RiotError contains the http status code of the erro
type RiotError struct {
	StatusCode int
}

// types for URL query parameter formatting
type strURLParameter []string
type intURLParameter []int64

// Creates a comma separated string
func (q strURLParameter) String() string {
	return strings.Join(q, ",")
}

// Convers int64 slice to string slice and returns
// comma separated string
func (q intURLParameter) String() string {
	s := make(strURLParameter, len(q))
	for k, v := range q {
		s[k] = strconv.FormatInt(v, 10)
	}

	return s.String()
}

//SetAPIKey sets the global key for the Riot Games API. If you do not have one you can get one for free at
//https://developer.riotgames.com/
func SetAPIKey(key string) {
	apikey = key
}

//SetSmallRateLimit allows a custom rate limit to be set. For at the time of this writing the default
//for a development API key is 10 requests every 10 seconds
func SetSmallRateLimit(numrequests int, pertime time.Duration) {
	smallRateChan = rateChan{
		RateQueue:   make(chan bool, numrequests),
		TriggerChan: make(chan bool),
	}
	go rateLimitHandler(smallRateChan, pertime)
}

//SetLongRateLimit allows a custom rate limit to be set. For at the time of this writing the default
//for a development API key is 500 requests every 10 minutes
func SetLongRateLimit(numrequests int, pertime time.Duration) {
	longRateChan = rateChan{
		RateQueue:   make(chan bool, numrequests),
		TriggerChan: make(chan bool),
	}
	go rateLimitHandler(longRateChan, pertime)
}

func rateLimitHandler(RateChan rateChan, pertime time.Duration) {
	returnChan := make(chan bool)
	go timeTriggerWatcher(RateChan.TriggerChan, returnChan)
	for {
		<-returnChan
		<-time.After(pertime)
		go timeTriggerWatcher(RateChan.TriggerChan, returnChan)
		length := len(RateChan.RateQueue)
		for i := 0; i < length; i++ {
			<-RateChan.RateQueue
		}
	}
}

func timeTriggerWatcher(timeTrigger chan bool, returnChan chan bool) {
	timeTrigger <- true
	returnChan <- true
}

//IsKeySet returns a bool of if the global API key has been set
func IsKeySet() bool {
	return !(apikey == "")
}

func requestAndUnmarshal(requestURL string, v interface{}) (err error) {
	checkRateLimiter(smallRateChan)
	checkRateLimiter(longRateChan)
	resp, err := http.Get(requestURL)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	checkTimeTrigger(smallRateChan)
	checkTimeTrigger(longRateChan)
	if resp.StatusCode != http.StatusOK {
		return RiotError{StatusCode: resp.StatusCode}
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	err = json.Unmarshal(body, v)
	if err != nil {
		return
	}
	return
}

func checkRateLimiter(RateChan rateChan) {
	if RateChan.RateQueue != nil && RateChan.TriggerChan != nil {
		RateChan.RateQueue <- true
	}
}

func checkTimeTrigger(RateChan rateChan) {
	if RateChan.RateQueue != nil && RateChan.TriggerChan != nil {
		select {
		case <-RateChan.TriggerChan:
		default:
		}
	}
}

//Error prints the error message for a RiotError
func (err RiotError) Error() string {
	return fmt.Sprintf("Error: HTTP Status %d", err.StatusCode)
}
<|endoftext|>


<commit_before>package main

import  (
        "fmt"
        "../go-todotxt"
        "github.com/spf13/cobra"
        "os/user"
        "strings"
        "strconv"
        "github.com/rakyll/globalconf"
        "flag"
)

func extendedLoader(filename string) (todotxt.TaskList, error) {
        usr, err := user.Current()
        if err != nil {
                return nil, err
        }

        filename = strings.Replace(filename, "~", usr.HomeDir, -1)
        tasks := todotxt.LoadTaskList(filename)

        return tasks, nil
}

func main() {

        conf, _ := globalconf.New("gotodo")

        var numtasks bool
        var sortby string
        var finished bool
        var prettyformat string
        var filename string

        var flagFilename = flag.String("file", "", "Location of the todo.txt file.")

        var cmdConfig = &cobra.Command{
            Use:   "config [key] [value]",
            Short: "Show and sets config values",
            Long:  `Config can be used to see and also set configuration variables.`,
            Run: func(cmd *cobra.Command, args []string) {
                fmt.Printf("%v\n", args)
            },
        }

        var cmdList = &cobra.Command{
            Use:   "list [keyword]",
            Short: "Lists tasks that contain keyword, if any",
            Long:  `List is the most basic command that is used for listing tasks.
                    You can specify a keyword as well as other options.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if numtasks {
                    fmt.Println(tasks.Len())
                } else {
                    tasks.Sort(sortby)

                    var filteredTasks todotxt.TaskList
                    for _, task := range tasks {
                        if (!task.Finished() && !finished) ||
                           (task.Finished() && finished) {
                           filteredTasks = append(filteredTasks, task)
                        }
                    }

                    for _, task := range filteredTasks {
                            task.SetIdPaddingBy(tasks)
                            fmt.Println(task.PrettyPrint(prettyformat))
                    }
                }
            },
        }
        cmdList.Flags().BoolVarP(&numtasks, "num-tasks", "n", false,
                                 "Show the number of tasks")
        cmdList.Flags().BoolVarP(&finished, "finished", "f", false,
                                 "Show finished tasks")
        cmdList.Flags().StringVarP(&sortby, "sort", "s", "prio",
                                   "Sort tasks by parameter (prio|date|len|prio-rev|date-rev|len-rev|id|rand)")
        cmdList.Flags().StringVarP(&prettyformat, "pretty", "", "%i %p %t",
                                   "Pretty print tasks")

        var cmdAdd = &cobra.Command{
            Use:   "add [task]",
            Short: "Adds a task to the todo list.",
            Long:  `Adds a task to the todo list.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                task := strings.Join(args, " ")
                tasks.Add(task)

                tasks.Save(filename)
            },
        }

        var nofinishdate bool
        var cmdDone = &cobra.Command{
            Use:   "done [taskid]",
            Short: "Marks task as done.",
            Long:  `Marks task as done.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what needs to be done?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                err = tasks.Done(taskid, !nofinishdate)
                if err != nil {
                        fmt.Printf("There was an error %v\n", err)
                }

                tasks.Save(filename)
            },
        }
        cmdDone.Flags().BoolVarP(&nofinishdate, "no-finish-date", "D", false,
                                        "Do not mark finished tasks with date.")

        var cmdArchive = &cobra.Command{
            Use:   "archive [taskid]",
            Short: "Archives task.",
            Long:  `Archives task.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what needs to be done?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                fmt.Printf("Archiving task %v\n", taskid)

                tasks.Save(filename)
            },
        }

        var editprio string
        var edittodo string
        var cmdEdit = &cobra.Command{
            Use:   "edit [taskid]",
            Short: "Edits given task.",
            Long:  `Edits given task.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what do you want to edit?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                if len(editprio) > 0 {
                        tasks[taskid].SetPriority(editprio[0])
                        tasks[taskid].RebuildRawTodo()
                }

                if len(edittodo) > 0 {
                        tasks[taskid].SetTodo(edittodo)
                        tasks[taskid].RebuildRawTodo()
                }

                tasks.Save(filename)
            },
        }

        cmdEdit.PersistentFlags().StringVarP(&editprio, "priority", "p", "",
                                     "Sets task's priority.")
        cmdEdit.PersistentFlags().StringVarP(&edittodo, "todo", "t", "",
                                     "Edit task's todo.")

        var GotodoCmd = &cobra.Command{
            Use:   "gotodo",
            Short: "Gotodo is a go implementation of todo.txt.",
            Long: `A small, fast and fun implementation of todo.txt`,
            Run: func(cmd *cobra.Command, args []string) {
                cmdList.Run(cmd, nil)
            },
        }

        GotodoCmd.PersistentFlags().StringVarP(&filename, "filename", "", "",
                                     "Load tasks from this file.")

        conf.ParseAll()

        // sadly, this is the best we can do right now
        if filename == "" {
                fmt.Println(*flagFilename)
                if *flagFilename == "" {
                        filename = "todo.txt"
                } else {
                        filename = *flagFilename
                }
        }

        GotodoCmd.AddCommand(cmdList)
        GotodoCmd.AddCommand(cmdAdd)
        GotodoCmd.AddCommand(cmdDone)
        GotodoCmd.AddCommand(cmdArchive)
        GotodoCmd.AddCommand(cmdEdit)
        GotodoCmd.AddCommand(cmdConfig)
        GotodoCmd.Execute()
}
<commit_msg>cleanup<commit_after>package main

import  (
        "fmt"
        "../go-todotxt"
        "github.com/spf13/cobra"
        "os/user"
        "strings"
        "strconv"
        "github.com/rakyll/globalconf"
        "flag"
)

func extendedLoader(filename string) (todotxt.TaskList, error) {
        usr, err := user.Current()
        if err != nil {
                return nil, err
        }

        filename = strings.Replace(filename, "~", usr.HomeDir, -1)
        tasks := todotxt.LoadTaskList(filename)

        return tasks, nil
}

func main() {

        conf, _ := globalconf.New("gotodo")

        var numtasks bool
        var sortby string
        var finished bool
        var prettyformat string
        var filename string

        var flagFilename = flag.String("file", "", "Location of the todo.txt file.")

        var cmdConfig = &cobra.Command{
            Use:   "config [key] [value]",
            Short: "Show and sets config values",
            Long:  `Config can be used to see and also set configuration variables.`,
            Run: func(cmd *cobra.Command, args []string) {
                fmt.Printf("%v\n", args)
            },
        }

        var cmdList = &cobra.Command{
            Use:   "list [keyword]",
            Short: "Lists tasks that contain keyword, if any",
            Long:  `List is the most basic command that is used for listing tasks.
                    You can specify a keyword as well as other options.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if numtasks {
                    fmt.Println(tasks.Len())
                } else {
                    tasks.Sort(sortby)

                    var filteredTasks todotxt.TaskList
                    for _, task := range tasks {
                        if (!task.Finished() && !finished) ||
                           (task.Finished() && finished) {
                           filteredTasks = append(filteredTasks, task)
                        }
                    }

                    for _, task := range filteredTasks {
                            task.SetIdPaddingBy(tasks)
                            fmt.Println(task.PrettyPrint(prettyformat))
                    }
                }
            },
        }
        cmdList.Flags().BoolVarP(&numtasks, "num-tasks", "n", false,
                                 "Show the number of tasks")
        cmdList.Flags().BoolVarP(&finished, "finished", "f", false,
                                 "Show finished tasks")
        cmdList.Flags().StringVarP(&sortby, "sort", "s", "prio",
                                   "Sort tasks by parameter (prio|date|len|prio-rev|date-rev|len-rev|id|rand)")
        cmdList.Flags().StringVarP(&prettyformat, "pretty", "", "%i %p %t",
                                   "Pretty print tasks")

        var cmdAdd = &cobra.Command{
            Use:   "add [task]",
            Short: "Adds a task to the todo list.",
            Long:  `Adds a task to the todo list.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                task := strings.Join(args, " ")
                tasks.Add(task)

                tasks.Save(filename)
            },
        }

        var nofinishdate bool
        var cmdDone = &cobra.Command{
            Use:   "done [taskid]",
            Short: "Marks task as done.",
            Long:  `Marks task as done.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what needs to be done?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                err = tasks.Done(taskid, !nofinishdate)
                if err != nil {
                        fmt.Printf("There was an error %v\n", err)
                }

                tasks.Save(filename)
            },
        }
        cmdDone.Flags().BoolVarP(&nofinishdate, "no-finish-date", "D", false,
                                        "Do not mark finished tasks with date.")

        var cmdArchive = &cobra.Command{
            Use:   "archive [taskid]",
            Short: "Archives task.",
            Long:  `Archives task.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what needs to be done?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                fmt.Printf("Archiving task %v\n", taskid)

                tasks.Save(filename)
            },
        }

        var editprio string
        var edittodo string
        var cmdEdit = &cobra.Command{
            Use:   "edit [taskid]",
            Short: "Edits given task.",
            Long:  `Edits given task.`,
            Run: func(cmd *cobra.Command, args []string) {
                tasks, err := extendedLoader(filename)
                if err != nil {
                        fmt.Println(err)
                        return
                }

                if len(args) < 1 {
                        fmt.Println("So what do you want to edit?")
                        return
                }

                taskid, err := strconv.Atoi(args[0])
                if err != nil {
                        fmt.Printf("Do you really consider that a number? %v\n", err)
                        return
                }

                if len(editprio) > 0 {
                        tasks[taskid].SetPriority(editprio[0])
                        tasks[taskid].RebuildRawTodo()
                }

                if len(edittodo) > 0 {
                        tasks[taskid].SetTodo(edittodo)
                        tasks[taskid].RebuildRawTodo()
                }

                tasks.Save(filename)
            },
        }

        cmdEdit.PersistentFlags().StringVarP(&editprio, "priority", "p", "",
                                     "Sets task's priority.")
        cmdEdit.PersistentFlags().StringVarP(&edittodo, "todo", "t", "",
                                     "Edit task's todo.")

        var GotodoCmd = &cobra.Command{
            Use:   "gotodo",
            Short: "Gotodo is a go implementation of todo.txt.",
            Long: `A small, fast and fun implementation of todo.txt`,
            Run: func(cmd *cobra.Command, args []string) {
                cmdList.Run(cmd, nil)
            },
        }

        GotodoCmd.PersistentFlags().StringVarP(&filename, "filename", "", "",
                                     "Load tasks from this file.")

        conf.ParseAll()

        // sadly, this is the best we can do right now
        if filename == "" {
                if *flagFilename == "" {
                        filename = "todo.txt"
                } else {
                        filename = *flagFilename
                }
        }

        GotodoCmd.AddCommand(cmdList)
        GotodoCmd.AddCommand(cmdAdd)
        GotodoCmd.AddCommand(cmdDone)
        GotodoCmd.AddCommand(cmdArchive)
        GotodoCmd.AddCommand(cmdEdit)
        GotodoCmd.AddCommand(cmdConfig)
        GotodoCmd.Execute()
}
<|endoftext|>


<commit_before>package highlighter

import (
	"bytes"
	"row"
	"strings"
	"unicode"
)

const (
	HL_NORMAL    = 0
	HL_COMMENT   = iota
	HL_MLCOMMENT = iota
	HL_KEYWORD1  = iota
	HL_KEYWORD2  = iota
	HL_STRING    = iota
	HL_NUMBER    = iota
	HL_MATCH     = iota
)

type Syntax struct {
	Filetype               string
	filematch              []string
	keywords               []string
	singleLineCommentStart []byte
	multiLineCommentStart  []byte
	multiLineCommentEnd    []byte
	flags                  int
}

var hldb = []Syntax{
	{
		Filetype:  "c",
		filematch: []string{".c", ".h", ".cpp"},
		keywords: []string{"switch", "if", "while", "for",
			"break", "continue", "return", "else", "struct",
			"union", "typedef", "static", "enum", "class", "case",
			"int|", "long|", "double|", "float|", "char|",
			"unsigned|", "signed|", "void|",
		},
		singleLineCommentStart: []byte{'/', '/'},
		multiLineCommentStart:  []byte{'/', '*'},
		multiLineCommentEnd:    []byte{'*', '/'},
		flags:                  HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS,
	},
	{
		Filetype:  "Go",
		filematch: []string{".go"},
		keywords: []string{"switch", "if", "for", "select",
			"break", "continue", "return", "else", "struct",
			"type", "case", "select", "func", "var", "import",
			"const",
			"int|", "long|", "double|", "float|", "char|", "byte|",
			"unsigned|", "signed|", "string|", "chan|", "bool|",
			"rune|",
		},
		singleLineCommentStart: []byte{'/', '/'},
		multiLineCommentStart:  []byte{'/', '*'},
		multiLineCommentEnd:    []byte{'*', '/'},
		flags:                  HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS,
	},
}

const (
	HL_HIGHLIGHT_NUMBERS = 1 << 0
	HL_HIGHLIGHT_STRINGS = 1 << iota
)

var separators = []byte(",.()+-/*=~%<>[]; \t\n\r")

func isSeparator(c byte) bool {
	return bytes.IndexByte(separators, c) >= 0
}

func (syntax *Syntax) UpdateSyntax(row *row.Row, inCommentNow bool) (updateNextRow bool) {
	row.Hl = make([]byte, row.Rsize)
	if syntax == nil {
		return
	}
	updateNextRow = false
	scs := syntax.singleLineCommentStart
	mcs := syntax.multiLineCommentStart
	mce := syntax.multiLineCommentEnd
	prevSep := true
	inComment := inCommentNow
	var inString byte
	var skip int
	for i, c := range row.Render {
		if skip > 0 {
			skip--
			continue
		}
		if inString == 0 && len(scs) > 0 && !inComment {
			if bytes.HasPrefix(row.Render[i:], scs) {
				for j := i; j < row.Rsize; j++ {
					row.Hl[j] = HL_COMMENT
				}
				break
			}
		}
		if inString == 0 && len(mcs) > 0 && len(mce) > 0 {
			if inComment {
				row.Hl[i] = HL_MLCOMMENT
				if bytes.HasPrefix(row.Render[i:], mce) {
					for l := i; l < i+len(mce); l++ {
						row.Hl[l] = HL_MLCOMMENT
					}
					skip = len(mce)
					inComment = false
					prevSep = true
				}
				continue
			} else if bytes.HasPrefix(row.Render[i:], mcs) {
				for l := i; l < i+len(mcs); l++ {
					row.Hl[l] = HL_MLCOMMENT
				}
				inComment = true
				skip = len(mcs)
			}
		}
		var prevHl byte = HL_NORMAL
		if i > 0 {
			prevHl = row.Hl[i-1]
		}
		if (syntax.flags & HL_HIGHLIGHT_STRINGS) == HL_HIGHLIGHT_STRINGS {
			if inString != 0 {
				row.Hl[i] = HL_STRING
				if c == '\\' && i+1 < row.Rsize {
					row.Hl[i+1] = HL_STRING
					skip = 1
					continue
				}
				if c == inString {
					inString = 0
				}
				prevSep = true
				continue
			} else {
				if c == '"' || c == '\'' {
					inString = c
					row.Hl[i] = HL_STRING
					continue
				}
			}
		}
		if (syntax.flags & HL_HIGHLIGHT_NUMBERS) == HL_HIGHLIGHT_NUMBERS {
			if unicode.IsDigit(rune(c)) &&
				(prevSep || prevHl == HL_NUMBER) ||
				(c == '.' && prevHl == HL_NUMBER) {
				row.Hl[i] = HL_NUMBER
				prevSep = false
				continue
			}
		}
		if prevSep {
			var j int
			var skw string
			for j, skw = range syntax.keywords[:] {
				kw := []byte(skw)
				var color byte = HL_KEYWORD1
				idx := bytes.LastIndexByte(kw, '|')
				if idx > 0 {
					kw = kw[:idx]
					color = HL_KEYWORD2
				}
				klen := len(kw)
				if bytes.HasPrefix(row.Render[i:], kw) &&
					(len(row.Render[i:]) == klen ||
						isSeparator(row.Render[i+klen])) {
					for l := i; l < i+klen; l++ {
						row.Hl[l] = color
					}
					skip = klen - 1
					break
				}
			}
			if j < len(syntax.keywords)-1 {
				prevSep = false
				continue
			}
		}
		prevSep = isSeparator(c)
	}

	updateNextRow = row.HlOpenComment != inComment
	row.HlOpenComment = inComment
	return updateNextRow
}

func SyntaxToColor(hl byte) int {
	switch hl {
	case HL_COMMENT, HL_MLCOMMENT:
		return 36
	case HL_KEYWORD1:
		return 32
	case HL_KEYWORD2:
		return 33
	case HL_STRING:
		return 35
	case HL_NUMBER:
		return 31
	case HL_MATCH:
		return 34
	}
	return 37
}

func SelectSyntaxHighlight(filename string) *Syntax {
	if filename == "" {
		return nil
	}

	for _, s := range hldb {
		for _, suffix := range s.filematch {
			if strings.HasSuffix(filename, suffix) {
				return &s
			}
		}
	}
	return nil
}
<commit_msg>Add comments so golint doesn't complain.<commit_after>package highlighter

import (
	"bytes"
	"row"
	"strings"
	"unicode"
)

const (
	HL_NORMAL    = 0
	HL_COMMENT   = iota
	HL_MLCOMMENT = iota
	HL_KEYWORD1  = iota
	HL_KEYWORD2  = iota
	HL_STRING    = iota
	HL_NUMBER    = iota
	HL_MATCH     = iota
)

// Syntax instances hold substrings used to do simple
// "syntax coloring" of a file under edit.
type Syntax struct {
	Filetype               string
	filematch              []string
	keywords               []string
	singleLineCommentStart []byte
	multiLineCommentStart  []byte
	multiLineCommentEnd    []byte
	flags                  int
}

var hldb = []Syntax{
	{
		Filetype:  "c",
		filematch: []string{".c", ".h", ".cpp"},
		keywords: []string{"switch", "if", "while", "for",
			"break", "continue", "return", "else", "struct",
			"union", "typedef", "static", "enum", "class", "case",
			"int|", "long|", "double|", "float|", "char|",
			"unsigned|", "signed|", "void|",
		},
		singleLineCommentStart: []byte{'/', '/'},
		multiLineCommentStart:  []byte{'/', '*'},
		multiLineCommentEnd:    []byte{'*', '/'},
		flags:                  HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS,
	},
	{
		Filetype:  "Go",
		filematch: []string{".go"},
		keywords: []string{"switch", "if", "for", "select",
			"break", "continue", "return", "else", "struct",
			"type", "case", "select", "func", "var", "import",
			"const",
			"int|", "long|", "double|", "float|", "char|", "byte|",
			"unsigned|", "signed|", "string|", "chan|", "bool|",
			"rune|",
		},
		singleLineCommentStart: []byte{'/', '/'},
		multiLineCommentStart:  []byte{'/', '*'},
		multiLineCommentEnd:    []byte{'*', '/'},
		flags:                  HL_HIGHLIGHT_NUMBERS | HL_HIGHLIGHT_STRINGS,
	},
}

const (
	HL_HIGHLIGHT_NUMBERS = 1 << 0
	HL_HIGHLIGHT_STRINGS = 1 << iota
)

var separators = []byte(",.()+-/*=~%<>[]; \t\n\r")

func isSeparator(c byte) bool {
	return bytes.IndexByte(separators, c) >= 0
}

// UpdateSyntax fills in the Row.Hl element with numbers that
// constitute the color the byte with the same index in Row.Render
// should have when displayed.
func (syntax *Syntax) UpdateSyntax(row *row.Row, inCommentNow bool) (updateNextRow bool) {
	row.Hl = make([]byte, row.Rsize)
	if syntax == nil {
		return
	}
	updateNextRow = false
	scs := syntax.singleLineCommentStart
	mcs := syntax.multiLineCommentStart
	mce := syntax.multiLineCommentEnd
	prevSep := true
	inComment := inCommentNow
	var inString byte
	var skip int
	for i, c := range row.Render {
		if skip > 0 {
			skip--
			continue
		}
		if inString == 0 && len(scs) > 0 && !inComment {
			if bytes.HasPrefix(row.Render[i:], scs) {
				for j := i; j < row.Rsize; j++ {
					row.Hl[j] = HL_COMMENT
				}
				break
			}
		}
		if inString == 0 && len(mcs) > 0 && len(mce) > 0 {
			if inComment {
				row.Hl[i] = HL_MLCOMMENT
				if bytes.HasPrefix(row.Render[i:], mce) {
					for l := i; l < i+len(mce); l++ {
						row.Hl[l] = HL_MLCOMMENT
					}
					skip = len(mce)
					inComment = false
					prevSep = true
				}
				continue
			} else if bytes.HasPrefix(row.Render[i:], mcs) {
				for l := i; l < i+len(mcs); l++ {
					row.Hl[l] = HL_MLCOMMENT
				}
				inComment = true
				skip = len(mcs)
			}
		}
		var prevHl byte = HL_NORMAL
		if i > 0 {
			prevHl = row.Hl[i-1]
		}
		if (syntax.flags & HL_HIGHLIGHT_STRINGS) == HL_HIGHLIGHT_STRINGS {
			if inString != 0 {
				row.Hl[i] = HL_STRING
				if c == '\\' && i+1 < row.Rsize {
					row.Hl[i+1] = HL_STRING
					skip = 1
					continue
				}
				if c == inString {
					inString = 0
				}
				prevSep = true
				continue
			} else {
				if c == '"' || c == '\'' {
					inString = c
					row.Hl[i] = HL_STRING
					continue
				}
			}
		}
		if (syntax.flags & HL_HIGHLIGHT_NUMBERS) == HL_HIGHLIGHT_NUMBERS {
			if unicode.IsDigit(rune(c)) &&
				(prevSep || prevHl == HL_NUMBER) ||
				(c == '.' && prevHl == HL_NUMBER) {
				row.Hl[i] = HL_NUMBER
				prevSep = false
				continue
			}
		}
		if prevSep {
			var j int
			var skw string
			for j, skw = range syntax.keywords[:] {
				kw := []byte(skw)
				var color byte = HL_KEYWORD1
				idx := bytes.LastIndexByte(kw, '|')
				if idx > 0 {
					kw = kw[:idx]
					color = HL_KEYWORD2
				}
				klen := len(kw)
				if bytes.HasPrefix(row.Render[i:], kw) &&
					(len(row.Render[i:]) == klen ||
						isSeparator(row.Render[i+klen])) {
					for l := i; l < i+klen; l++ {
						row.Hl[l] = color
					}
					skip = klen - 1
					break
				}
			}
			if j < len(syntax.keywords)-1 {
				prevSep = false
				continue
			}
		}
		prevSep = isSeparator(c)
	}

	updateNextRow = row.HlOpenComment != inComment
	row.HlOpenComment = inComment
	return updateNextRow
}

// SyntaxToColor maps byte values from Row.Hl to the color numbers
// used in VT-100 escape sequences.
func SyntaxToColor(hl byte) int {
	switch hl {
	case HL_COMMENT, HL_MLCOMMENT:
		return 36
	case HL_KEYWORD1:
		return 32
	case HL_KEYWORD2:
		return 33
	case HL_STRING:
		return 35
	case HL_NUMBER:
		return 31
	case HL_MATCH:
		return 34
	}
	return 37
}

// SelectSyntaxHighlight selects an element of array hldb (*Syntax)
// based on the suffix of the filename argument.
func SelectSyntaxHighlight(filename string) *Syntax {
	if filename == "" {
		return nil
	}

	for _, s := range hldb {
		for _, suffix := range s.filematch {
			if strings.HasSuffix(filename, suffix) {
				return &s
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package openflow

import (
	"io"
	"math/rand"

	"github.com/netrack/openflow/internal/encoding"
)

const (
	// Immutable messages.
	TypeHello Type = iota
	TypeError
	TypeEchoRequest
	TypeEchoReply
	TypeExperiment

	// Switch configuration messages.
	TypeFeaturesRequest
	TypeFeaturesReply
	TypeGetConfigRequest
	TypeGetConfigReply
	TypeSetConfig

	// Asynchronous messages.
	TypePacketIn
	TypeFlowRemoved
	TypePortStatus

	// Controller command messages.
	TypePacketOut
	TypeFlowMod
	TypeGroupMod
	TypePortMod
	TypeTableMod

	// Multipart messages
	TypeMultipartRequest
	TypeMultipartReply

	// Queue configuration messages.
	TypeQueueGetConfigRequest
	TypeQueueGetConfigReply

	// Controller role change request messages.
	TypeRoleRequest
	TypeRoleReply

	// Asynchronous message configuration.
	TypeAsynchRequest
	TypeAsyncReply
	TypeSetAsync

	// Meters and rate limiters configuration messages.
	TypeMeterMod
)

type Type uint8

// The Header is a response header. It contains the negotiated
// version of the OpenFlow, a type and length of the message.
type Header struct {
	// Version specifies the version of the protocol.
	Version uint8

	// Type defines a type of the message.
	Type Type

	// Length including this Header.
	Length uint16

	// Transaction is an transaction ID associated with this packet.
	//
	// Replies use the same id as was in the request to facilitate pairing.
	Transaction uint32
}

func (h *Header) Copy() *Header {
	return &Header{h.Version, h.Type, h.Length, h.Transaction}
}

// Length of the packet payload including header.
func (h *Header) Len() int {
	return int(h.Length)
}

// WriteTo writes the header in the write format to the given writer.
func (h *Header) WriteTo(w io.Writer) (int64, error) {
	return encoding.WriteTo(w, *h)
}

// ReadFrom reads the header from the given reader in the wire format.
func (h *Header) ReadFrom(r io.Reader) (int64, error) {
	return encoding.ReadFrom(r, &h.Version, &h.Type, &h.Length, &h.Transaction)
}

// TransactionMatcher creates a new matcher that matches the request
// by the transaction identifier.
//
// If the header has non-zero transaction identifier, it will be used
// to create a new matcher, otherwise a random number will be generated.
func TransactionMatcher(h *Header) Matcher {
	// When the trasnaction is not defined, we will generate a
	// random number, that will be used to match the response.
	if h.Transaction == 0 {
		h.Transaction = rand.Uint32()
	}

	transaction := h.Transaction
	matcher := func(r *Request) bool {
		return r.Header.Transaction == transaction
	}

	// Return a function wrapped into the function adapter.
	return &MatcherFunc{matcher}
}
<commit_msg>Add barrier messages (#116)<commit_after>package openflow

import (
	"io"
	"math/rand"

	"github.com/netrack/openflow/internal/encoding"
)

const (
	// Immutable messages.
	TypeHello Type = iota
	TypeError
	TypeEchoRequest
	TypeEchoReply
	TypeExperiment

	// Switch configuration messages.
	TypeFeaturesRequest
	TypeFeaturesReply
	TypeGetConfigRequest
	TypeGetConfigReply
	TypeSetConfig

	// Asynchronous messages.
	TypePacketIn
	TypeFlowRemoved
	TypePortStatus

	// Controller command messages.
	TypePacketOut
	TypeFlowMod
	TypeGroupMod
	TypePortMod
	TypeTableMod

	// Multipart messages
	TypeMultipartRequest
	TypeMultipartReply

	// Barrier messages
	TypeBarrierRequest
	TypeBarrierReply

	// Queue configuration messages.
	TypeQueueGetConfigRequest
	TypeQueueGetConfigReply

	// Controller role change request messages.
	TypeRoleRequest
	TypeRoleReply

	// Asynchronous message configuration.
	TypeAsynchRequest
	TypeAsyncReply
	TypeSetAsync

	// Meters and rate limiters configuration messages.
	TypeMeterMod
)

type Type uint8

// The Header is a response header. It contains the negotiated
// version of the OpenFlow, a type and length of the message.
type Header struct {
	// Version specifies the version of the protocol.
	Version uint8

	// Type defines a type of the message.
	Type Type

	// Length including this Header.
	Length uint16

	// Transaction is an transaction ID associated with this packet.
	//
	// Replies use the same id as was in the request to facilitate pairing.
	Transaction uint32
}

func (h *Header) Copy() *Header {
	return &Header{h.Version, h.Type, h.Length, h.Transaction}
}

// Length of the packet payload including header.
func (h *Header) Len() int {
	return int(h.Length)
}

// WriteTo writes the header in the write format to the given writer.
func (h *Header) WriteTo(w io.Writer) (int64, error) {
	return encoding.WriteTo(w, *h)
}

// ReadFrom reads the header from the given reader in the wire format.
func (h *Header) ReadFrom(r io.Reader) (int64, error) {
	return encoding.ReadFrom(r, &h.Version, &h.Type, &h.Length, &h.Transaction)
}

// TransactionMatcher creates a new matcher that matches the request
// by the transaction identifier.
//
// If the header has non-zero transaction identifier, it will be used
// to create a new matcher, otherwise a random number will be generated.
func TransactionMatcher(h *Header) Matcher {
	// When the trasnaction is not defined, we will generate a
	// random number, that will be used to match the response.
	if h.Transaction == 0 {
		h.Transaction = rand.Uint32()
	}

	transaction := h.Transaction
	matcher := func(r *Request) bool {
		return r.Header.Transaction == transaction
	}

	// Return a function wrapped into the function adapter.
	return &MatcherFunc{matcher}
}
<|endoftext|>


<commit_before>package response

import (
	"encoding/json"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
)

const viewsDir = "views"

var views *template.Template

func init() {
	views = template.New("views")

	// Built-in views funcs
	views.Funcs(template.FuncMap{
		"html":  viewsFuncHTML,
		"nl2br": viewsFuncNL2BR,
	})

	core.BeforeRun(func() {
		if _, err := os.Stat(viewsDir); err != nil {
			return
		}

		if err := filepath.Walk(viewsDir, walk); err != nil {
			panic(err)
		}
	})
}

// walk is the path/filepath.WalkFunc that is used to walk viewsDir in order to initialize
// views. It will try to parse all files it encounters and recurse into subdirectories.
func walk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = views.ParseFiles(path)

	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// ViewsFuncs adds a function that will be available to all templates.
func ViewsFuncs(funcMap FuncMap) {
	views.Funcs(template.FuncMap(funcMap))
}

// Status responds with the given status code.
func Status(c *core.Context, v int) {
	http.Error(c.ResponseWriter, http.StatusText(v), v)
}

// String responds with the given string.
func String(c *core.Context, s string) {
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the given slice of byte.
func Bytes(c *core.Context, b []byte) {
	c.ResponseWriter.Write(b)
}

// JSON set the correct header and responds with the marshalled content.
func JSON(c *core.Context, v interface{}) {
	c.ResponseWriter.Header().Set("Content-Type", "application/json;charset=utf-8")
	if err := json.NewEncoder(c.ResponseWriter).Encode(v); err != nil {
		log.Println(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}

// View pass the data to the template associated to name, and responds with it.
func View(c *core.Context, name string, data map[string]interface{}) {
	data["c"] = c
	err := views.ExecuteTemplate(c.ResponseWriter, name, data)
	if err != nil {
		log.Println(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}
<commit_msg>Remove JSON charset<commit_after>package response

import (
	"encoding/json"
	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/volatile/core"
)

const viewsDir = "views"

var views *template.Template

func init() {
	views = template.New("views")

	// Built-in views funcs
	views.Funcs(template.FuncMap{
		"html":  viewsFuncHTML,
		"nl2br": viewsFuncNL2BR,
	})

	core.BeforeRun(func() {
		if _, err := os.Stat(viewsDir); err != nil {
			return
		}

		if err := filepath.Walk(viewsDir, walk); err != nil {
			panic(err)
		}
	})
}

// walk is the path/filepath.WalkFunc used to walk viewsDir in order to initialize views.
// It will try to parse all files it encounters and recurse into subdirectories.
func walk(path string, f os.FileInfo, err error) error {
	if err != nil {
		return err
	}

	if f.IsDir() {
		return nil
	}

	_, err = views.ParseFiles(path)

	return err
}

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of which the second has type error.
// In that case, if the second (error) argument evaluates to non-nil during execution, execution terminates and Execute returns that error.
// FuncMap has the same base type as FuncMap in "text/template", copied here so clients need not import "text/template".
type FuncMap map[string]interface{}

// ViewsFuncs adds a function that will be available to all templates.
func ViewsFuncs(funcMap FuncMap) {
	views.Funcs(template.FuncMap(funcMap))
}

// Status responds with the given status code.
func Status(c *core.Context, v int) {
	http.Error(c.ResponseWriter, http.StatusText(v), v)
}

// String responds with the given string.
func String(c *core.Context, s string) {
	c.ResponseWriter.Write([]byte(s))
}

// Bytes responds with the given slice of byte.
func Bytes(c *core.Context, b []byte) {
	c.ResponseWriter.Write(b)
}

// JSON set the correct header and responds with the marshalled content.
func JSON(c *core.Context, v interface{}) {
	c.ResponseWriter.Header().Set("Content-Type", "application/json")
	if err := json.NewEncoder(c.ResponseWriter).Encode(v); err != nil {
		log.Println(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}

// View pass the data to the template associated to name, and responds with it.
func View(c *core.Context, name string, data map[string]interface{}) {
	data["c"] = c
	err := views.ExecuteTemplate(c.ResponseWriter, name, data)
	if err != nil {
		log.Println(err)
		http.Error(c.ResponseWriter, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
	}
}
<|endoftext|>


<commit_before>// Package hiprus provides a Hipchat hook for the logrus loggin package.
package hiprus

import (
	"github.com/Sirupsen/logrus"
	"github.com/andybons/hipchat"
)

const (
	VERISON = "1.0.1"
)

// HiprusHook is a logrus Hook for dispatching messages to the specified
// channel on Hipchat.
type HiprusHook struct {
	// Messages with a log level not contained in this array
	// will not be dispatched. If nil, all messages will be dispatched.
	AcceptedLevels []logrus.Level
	AuthToken      string
	RoomName       string
	// If empty, "Hiprus" will be used.
	Username string
	c        *hipchat.Client
}

func (hh *HiprusHook) Levels() []logrus.Level {
	if hh.AcceptedLevels == nil {
		return AllLevels
	}
	return hh.AcceptedLevels
}

func (hh *HiprusHook) Fire(e *logrus.Entry) error {
	if hh.c == nil {
		if err := hh.initClient(); err != nil {
			return err
		}
	}

	color := ""
	switch e.Level {
	case logrus.DebugLevel:
		color = hipchat.ColorPurple
	case logrus.InfoLevel:
		color = hipchat.ColorGreen
	case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
		color = hipchat.ColorRed
	default:
		color = hipchat.ColorYellow
	}

	return hh.c.PostMessage(hipchat.MessageRequest{
		RoomId:        hh.RoomName,
		From:          hh.Username,
		Message:       e.Message,
		MessageFormat: "text",
		Notify:        true,
		Color:         color,
	})
}

func (hh *HiprusHook) initClient() error {
	hh.c = &hipchat.Client{hh.AuthToken}

	if hh.Username == "" {
		hh.Username = "HipRus"
	}

	return nil
}
<commit_msg>fix(initClient) Using NewClient() function<commit_after>// Package hiprus provides a Hipchat hook for the logrus loggin package.
package hiprus

import (
	"github.com/Sirupsen/logrus"
	"github.com/andybons/hipchat"
)

const (
	VERISON = "1.0.1"
)

// HiprusHook is a logrus Hook for dispatching messages to the specified
// channel on Hipchat.
type HiprusHook struct {
	// Messages with a log level not contained in this array
	// will not be dispatched. If nil, all messages will be dispatched.
	AcceptedLevels []logrus.Level
	AuthToken      string
	RoomName       string
	// If empty, "Hiprus" will be used.
	Username string
	c        *hipchat.Client
}

func (hh *HiprusHook) Levels() []logrus.Level {
	if hh.AcceptedLevels == nil {
		return AllLevels
	}
	return hh.AcceptedLevels
}

func (hh *HiprusHook) Fire(e *logrus.Entry) error {
	if hh.c == nil {
		if err := hh.initClient(); err != nil {
			return err
		}
	}

	color := ""
	switch e.Level {
	case logrus.DebugLevel:
		color = hipchat.ColorPurple
	case logrus.InfoLevel:
		color = hipchat.ColorGreen
	case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
		color = hipchat.ColorRed
	default:
		color = hipchat.ColorYellow
	}

	return hh.c.PostMessage(hipchat.MessageRequest{
		RoomId:        hh.RoomName,
		From:          hh.Username,
		Message:       e.Message,
		MessageFormat: "text",
		Notify:        true,
		Color:         color,
	})
}

func (hh *HiprusHook) initClient() error {
	c := hipchat.NewClient(hh.AuthToken)
	hh.c = &c

	if hh.Username == "" {
		hh.Username = "HipRus"
	}

	return nil
}
<|endoftext|>


<commit_before>package codearea

import (
	"errors"
	"reflect"
	"testing"

	"github.com/elves/elvish/cli/term"
	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/styled"
)

var bb = ui.NewBufferBuilder

var renderTests = []struct {
	name    string
	widget  *Widget
	width   int
	height  int
	wantBuf *ui.BufferBuilder
}{
	{
		"prompt only",
		&Widget{State: State{
			Prompt: styled.MakeText("~>", "bold")}},
		10, 24,
		bb(10).WriteString("~>", "1").SetDotToCursor(),
	},
	{
		"rprompt only",
		&Widget{State: State{
			RPrompt: styled.MakeText("RP", "inverse")}},
		10, 24,
		bb(10).SetDotToCursor().WriteSpaces(8, "").WriteString("RP", "7"),
	},
	{
		"code only with dot at beginning",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 0}}},
		10, 24,
		bb(10).SetDotToCursor().WritePlain("code"),
	},
	{
		"code only with dot at middle",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 2}}},
		10, 24,
		bb(10).WritePlain("co").SetDotToCursor().WritePlain("de"),
	},
	{
		"code only with dot at end",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 4}}},
		10, 24,
		bb(10).WritePlain("code").SetDotToCursor(),
	},
	{
		"prompt, code and rprompt",
		&Widget{State: State{
			Prompt:     styled.Plain("~>"),
			CodeBuffer: CodeBuffer{Content: "code", Dot: 4},
			RPrompt:    styled.Plain("RP"),
		}},
		10, 24,
		bb(10).WritePlain("~>code").SetDotToCursor().WritePlain("  RP"),
	},
	{
		"rprompt too long",
		&Widget{State: State{
			Prompt:     styled.Plain("~>"),
			CodeBuffer: CodeBuffer{Content: "code", Dot: 4},
			RPrompt:    styled.Plain("1234"),
		}},
		10, 24,
		bb(10).WritePlain("~>code").SetDotToCursor(),
	},
	{
		"highlighted code",
		&Widget{
			State: State{CodeBuffer: CodeBuffer{Content: "code", Dot: 4}},
			Highlighter: func(code string) (styled.Text, []error) {
				return styled.MakeText(code, "bold"), nil
			},
		},
		10, 24,
		bb(10).WriteString("code", "1").SetDotToCursor(),
	},
	{
		"static errors in code",
		&Widget{
			State: State{CodeBuffer: CodeBuffer{Content: "code", Dot: 4}},
			Highlighter: func(code string) (styled.Text, []error) {
				err := errors.New("static error")
				return styled.Plain(code), []error{err}
			},
		},
		10, 24,
		bb(10).WritePlain("code").SetDotToCursor().
			Newline().WritePlain("static error"),
	},
	{
		"pending code inserting at the dot",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 4},
			PendingCode: PendingCode{From: 4, To: 4, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("code").WriteString("x", "4").SetDotToCursor(),
	},
	{
		"pending code replacing at the dot",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 2},
			PendingCode: PendingCode{From: 2, To: 4, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("co").WriteString("x", "4").SetDotToCursor(),
	},
	{
		"pending code to the left of the dot",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 4},
			PendingCode: PendingCode{From: 1, To: 3, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("c").WriteString("x", "4").WritePlain("e").SetDotToCursor(),
	},
	{
		"pending code to the right of the cursor",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 1},
			PendingCode: PendingCode{From: 2, To: 3, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("c").SetDotToCursor().WritePlain("o").
			WriteString("x", "4").WritePlain("e"),
	},
	{
		"ignore invalid pending code",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 4},
			PendingCode: PendingCode{From: 2, To: 1, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("code").SetDotToCursor(),
	},
	{
		"prioritize lines before the cursor with small height",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "a\nb\nc\nd", Dot: 3},
		}},
		10, 2,
		bb(10).WritePlain("a").Newline().WritePlain("b").SetDotToCursor(),
	},
	{
		"show only the cursor line when height is 1",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "a\nb\nc\nd", Dot: 3},
		}},
		10, 1,
		bb(10).WritePlain("b").SetDotToCursor(),
	},
	{
		"show lines after the cursor when all lines before the cursor are shown",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "a\nb\nc\nd", Dot: 3},
		}},
		10, 3,
		bb(10).WritePlain("a").Newline().WritePlain("b").SetDotToCursor().
			Newline().WritePlain("c"),
	},
}

func TestRender(t *testing.T) {
	for _, test := range renderTests {
		t.Run(test.name, func(t *testing.T) {
			buf := test.widget.Render(test.width, test.height)
			wantBuf := test.wantBuf.Buffer()
			if !reflect.DeepEqual(buf, wantBuf) {
				t.Errorf("got buf %v, want %v", buf, wantBuf)
			}
		})
	}
}

var handleTests = []struct {
	name      string
	widget    *Widget
	events    []term.Event
	wantState State
}{
	{
		"simple inserts",
		&Widget{},
		[]term.Event{term.K('c'), term.K('o'), term.K('d'), term.K('e')},
		State{CodeBuffer: CodeBuffer{Content: "code", Dot: 4}},
	},
	{
		"unicode inserts",
		&Widget{},
		[]term.Event{term.K('你'), term.K('好')},
		State{CodeBuffer: CodeBuffer{Content: "你好", Dot: 6}},
	},
	{
		"unterminated paste",
		&Widget{},
		[]term.Event{term.PasteSetting(true), term.K('"'), term.K('x')},
		State{},
	},
	{
		"literal paste",
		&Widget{},
		[]term.Event{
			term.PasteSetting(true),
			term.K('"'), term.K('x'),
			term.PasteSetting(false)},
		State{CodeBuffer: CodeBuffer{Content: "\"x", Dot: 2}},
	},
	{
		"literal paste swallowing functional keys",
		&Widget{},
		[]term.Event{
			term.PasteSetting(true),
			term.K('a'), term.K(ui.F1), term.K('b'),
			term.PasteSetting(false)},
		State{CodeBuffer: CodeBuffer{Content: "ab", Dot: 2}},
	},
	{
		"quoted paste",
		&Widget{QuotePaste: func() bool { return true }},
		[]term.Event{
			term.PasteSetting(true),
			term.K('"'), term.K('x'),
			term.PasteSetting(false)},
		State{CodeBuffer: CodeBuffer{Content: "'\"x'", Dot: 4}},
	},
	{
		"backspace at end of code",
		&Widget{},
		[]term.Event{
			term.K('c'), term.K('o'), term.K('d'), term.K('e'),
			term.K(ui.Backspace)},
		State{CodeBuffer: CodeBuffer{Content: "cod", Dot: 3}},
	},
	{
		"backspace at middle of buffer",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 2}}},
		[]term.Event{term.K(ui.Backspace)},
		State{CodeBuffer: CodeBuffer{Content: "cde", Dot: 1}},
	},
	{
		"backspace at beginning of buffer",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 0}}},
		[]term.Event{term.K(ui.Backspace)},
		State{CodeBuffer: CodeBuffer{Content: "code", Dot: 0}},
	},
	{
		"backspace deleting unicode character",
		&Widget{},
		[]term.Event{
			term.K('你'), term.K('好'), term.K(ui.Backspace)},
		State{CodeBuffer: CodeBuffer{Content: "你", Dot: 3}},
	},
	{
		"abbreviation expansion",
		&Widget{
			Abbreviations: func(f func(abbr, full string)) {
				f("dn", "/dev/null")
			},
		},
		[]term.Event{term.K('d'), term.K('n')},
		State{CodeBuffer: CodeBuffer{Content: "/dev/null", Dot: 9}},
	},
	{
		"abbreviation expansion preferring longest",
		&Widget{
			Abbreviations: func(f func(abbr, full string)) {
				f("n", "none")
				f("dn", "/dev/null")
			},
		},
		[]term.Event{term.K('d'), term.K('n')},
		State{CodeBuffer: CodeBuffer{Content: "/dev/null", Dot: 9}},
	},
	{
		"abbreviation expansion interrupted by function key",
		&Widget{
			Abbreviations: func(f func(abbr, full string)) {
				f("dn", "/dev/null")
			},
		},
		[]term.Event{term.K('d'), term.K(ui.F1), term.K('n')},
		State{CodeBuffer: CodeBuffer{Content: "dn", Dot: 2}},
	},
}

func TestHandle(t *testing.T) {
	for _, test := range handleTests {
		t.Run(test.name, func(t *testing.T) {
			w := test.widget
			for _, event := range test.events {
				w.Handle(event)
			}
			if !reflect.DeepEqual(w.State, test.wantState) {
				t.Errorf("got state %v, want %v", w.State, test.wantState)
			}
		})
	}
}

var unhandledEvents = []term.Event{
	// Mouse events are unhandled
	term.MouseEvent{},
	// Function keys are unhandled (except Backspace)
	term.K(ui.F1),
	term.K('X', ui.Ctrl),
}

func TestHandle_UnhandledEvents(t *testing.T) {
	w := &Widget{}
	for _, event := range unhandledEvents {
		handled := w.Handle(event)
		if handled {
			t.Errorf("event %v got handled", event)
		}
	}
}

func TestHandle_AbbreviationExpansionInterruptedByExternalMutation(t *testing.T) {
	w := &Widget{
		Abbreviations: func(f func(abbr, full string)) {
			f("dn", "/dev/null")
		},
	}
	w.Handle(term.K('d'))
	w.State.MutateCodeBuffer(func(c *CodeBuffer) { c.InsertAtDot("d") })
	w.Handle(term.K('n'))
	wantState := State{CodeBuffer: CodeBuffer{Content: "ddn", Dot: 3}}
	if !reflect.DeepEqual(w.State, wantState) {
		t.Errorf("got state %v, want %v", w.State, wantState)
	}
}

func TestHandle_EnterEmitsSubmit(t *testing.T) {
	var submitted string
	w := &Widget{
		State:    State{CodeBuffer: CodeBuffer{Content: "code", Dot: 4}},
		OnSubmit: func(code string) { submitted = code },
	}
	w.Handle(term.K('\n'))
	if submitted != "code" {
		t.Errorf("code not submitted")
	}
}
<commit_msg>cli/codearea: Coverage golfing.<commit_after>package codearea

import (
	"errors"
	"reflect"
	"testing"

	"github.com/elves/elvish/cli/term"
	"github.com/elves/elvish/edit/ui"
	"github.com/elves/elvish/styled"
)

var bb = ui.NewBufferBuilder

var renderTests = []struct {
	name    string
	widget  *Widget
	width   int
	height  int
	wantBuf *ui.BufferBuilder
}{
	{
		"prompt only",
		&Widget{State: State{
			Prompt: styled.MakeText("~>", "bold")}},
		10, 24,
		bb(10).WriteString("~>", "1").SetDotToCursor(),
	},
	{
		"rprompt only",
		&Widget{State: State{
			RPrompt: styled.MakeText("RP", "inverse")}},
		10, 24,
		bb(10).SetDotToCursor().WriteSpaces(8, "").WriteString("RP", "7"),
	},
	{
		"code only with dot at beginning",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 0}}},
		10, 24,
		bb(10).SetDotToCursor().WritePlain("code"),
	},
	{
		"code only with dot at middle",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 2}}},
		10, 24,
		bb(10).WritePlain("co").SetDotToCursor().WritePlain("de"),
	},
	{
		"code only with dot at end",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 4}}},
		10, 24,
		bb(10).WritePlain("code").SetDotToCursor(),
	},
	{
		"prompt, code and rprompt",
		&Widget{State: State{
			Prompt:     styled.Plain("~>"),
			CodeBuffer: CodeBuffer{Content: "code", Dot: 4},
			RPrompt:    styled.Plain("RP"),
		}},
		10, 24,
		bb(10).WritePlain("~>code").SetDotToCursor().WritePlain("  RP"),
	},
	{
		"rprompt too long",
		&Widget{State: State{
			Prompt:     styled.Plain("~>"),
			CodeBuffer: CodeBuffer{Content: "code", Dot: 4},
			RPrompt:    styled.Plain("1234"),
		}},
		10, 24,
		bb(10).WritePlain("~>code").SetDotToCursor(),
	},
	{
		"highlighted code",
		&Widget{
			State: State{CodeBuffer: CodeBuffer{Content: "code", Dot: 4}},
			Highlighter: func(code string) (styled.Text, []error) {
				return styled.MakeText(code, "bold"), nil
			},
		},
		10, 24,
		bb(10).WriteString("code", "1").SetDotToCursor(),
	},
	{
		"static errors in code",
		&Widget{
			State: State{CodeBuffer: CodeBuffer{Content: "code", Dot: 4}},
			Highlighter: func(code string) (styled.Text, []error) {
				err := errors.New("static error")
				return styled.Plain(code), []error{err}
			},
		},
		10, 24,
		bb(10).WritePlain("code").SetDotToCursor().
			Newline().WritePlain("static error"),
	},
	{
		"pending code inserting at the dot",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 4},
			PendingCode: PendingCode{From: 4, To: 4, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("code").WriteString("x", "4").SetDotToCursor(),
	},
	{
		"pending code replacing at the dot",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 2},
			PendingCode: PendingCode{From: 2, To: 4, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("co").WriteString("x", "4").SetDotToCursor(),
	},
	{
		"pending code to the left of the dot",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 4},
			PendingCode: PendingCode{From: 1, To: 3, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("c").WriteString("x", "4").WritePlain("e").SetDotToCursor(),
	},
	{
		"pending code to the right of the cursor",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 1},
			PendingCode: PendingCode{From: 2, To: 3, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("c").SetDotToCursor().WritePlain("o").
			WriteString("x", "4").WritePlain("e"),
	},
	{
		"ignore invalid pending code",
		&Widget{State: State{
			CodeBuffer:  CodeBuffer{Content: "code", Dot: 4},
			PendingCode: PendingCode{From: 2, To: 1, Content: "x"},
		}},
		10, 24,
		bb(10).WritePlain("code").SetDotToCursor(),
	},
	{
		"prioritize lines before the cursor with small height",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "a\nb\nc\nd", Dot: 3},
		}},
		10, 2,
		bb(10).WritePlain("a").Newline().WritePlain("b").SetDotToCursor(),
	},
	{
		"show only the cursor line when height is 1",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "a\nb\nc\nd", Dot: 3},
		}},
		10, 1,
		bb(10).WritePlain("b").SetDotToCursor(),
	},
	{
		"show lines after the cursor when all lines before the cursor are shown",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "a\nb\nc\nd", Dot: 3},
		}},
		10, 3,
		bb(10).WritePlain("a").Newline().WritePlain("b").SetDotToCursor().
			Newline().WritePlain("c"),
	},
}

func TestRender(t *testing.T) {
	for _, test := range renderTests {
		t.Run(test.name, func(t *testing.T) {
			buf := test.widget.Render(test.width, test.height)
			wantBuf := test.wantBuf.Buffer()
			if !reflect.DeepEqual(buf, wantBuf) {
				t.Errorf("got buf %v, want %v", buf, wantBuf)
			}
		})
	}
}

var handleTests = []struct {
	name      string
	widget    *Widget
	events    []term.Event
	wantState State
}{
	{
		"simple inserts",
		&Widget{},
		[]term.Event{term.K('c'), term.K('o'), term.K('d'), term.K('e')},
		State{CodeBuffer: CodeBuffer{Content: "code", Dot: 4}},
	},
	{
		"unicode inserts",
		&Widget{},
		[]term.Event{term.K('你'), term.K('好')},
		State{CodeBuffer: CodeBuffer{Content: "你好", Dot: 6}},
	},
	{
		"unterminated paste",
		&Widget{},
		[]term.Event{term.PasteSetting(true), term.K('"'), term.K('x')},
		State{},
	},
	{
		"literal paste",
		&Widget{},
		[]term.Event{
			term.PasteSetting(true),
			term.K('"'), term.K('x'),
			term.PasteSetting(false)},
		State{CodeBuffer: CodeBuffer{Content: "\"x", Dot: 2}},
	},
	{
		"literal paste swallowing functional keys",
		&Widget{},
		[]term.Event{
			term.PasteSetting(true),
			term.K('a'), term.K(ui.F1), term.K('b'),
			term.PasteSetting(false)},
		State{CodeBuffer: CodeBuffer{Content: "ab", Dot: 2}},
	},
	{
		"quoted paste",
		&Widget{QuotePaste: func() bool { return true }},
		[]term.Event{
			term.PasteSetting(true),
			term.K('"'), term.K('x'),
			term.PasteSetting(false)},
		State{CodeBuffer: CodeBuffer{Content: "'\"x'", Dot: 4}},
	},
	{
		"backspace at end of code",
		&Widget{},
		[]term.Event{
			term.K('c'), term.K('o'), term.K('d'), term.K('e'),
			term.K(ui.Backspace)},
		State{CodeBuffer: CodeBuffer{Content: "cod", Dot: 3}},
	},
	{
		"backspace at middle of buffer",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 2}}},
		[]term.Event{term.K(ui.Backspace)},
		State{CodeBuffer: CodeBuffer{Content: "cde", Dot: 1}},
	},
	{
		"backspace at beginning of buffer",
		&Widget{State: State{
			CodeBuffer: CodeBuffer{Content: "code", Dot: 0}}},
		[]term.Event{term.K(ui.Backspace)},
		State{CodeBuffer: CodeBuffer{Content: "code", Dot: 0}},
	},
	{
		"backspace deleting unicode character",
		&Widget{},
		[]term.Event{
			term.K('你'), term.K('好'), term.K(ui.Backspace)},
		State{CodeBuffer: CodeBuffer{Content: "你", Dot: 3}},
	},
	{
		"abbreviation expansion",
		&Widget{
			Abbreviations: func(f func(abbr, full string)) {
				f("dn", "/dev/null")
			},
		},
		[]term.Event{term.K('d'), term.K('n')},
		State{CodeBuffer: CodeBuffer{Content: "/dev/null", Dot: 9}},
	},
	{
		"abbreviation expansion preferring longest",
		&Widget{
			Abbreviations: func(f func(abbr, full string)) {
				f("n", "none")
				f("dn", "/dev/null")
			},
		},
		[]term.Event{term.K('d'), term.K('n')},
		State{CodeBuffer: CodeBuffer{Content: "/dev/null", Dot: 9}},
	},
	{
		"abbreviation expansion interrupted by function key",
		&Widget{
			Abbreviations: func(f func(abbr, full string)) {
				f("dn", "/dev/null")
			},
		},
		[]term.Event{term.K('d'), term.K(ui.F1), term.K('n')},
		State{CodeBuffer: CodeBuffer{Content: "dn", Dot: 2}},
	},
}

func TestHandle(t *testing.T) {
	for _, test := range handleTests {
		t.Run(test.name, func(t *testing.T) {
			w := test.widget
			for _, event := range test.events {
				w.Handle(event)
			}
			if !reflect.DeepEqual(w.State, test.wantState) {
				t.Errorf("got state %v, want %v", w.State, test.wantState)
			}
		})
	}
}

var unhandledEvents = []term.Event{
	// Mouse events are unhandled
	term.MouseEvent{},
	// Function keys are unhandled (except Backspace)
	term.K(ui.F1),
	term.K('X', ui.Ctrl),
}

func TestHandle_UnhandledEvents(t *testing.T) {
	w := &Widget{}
	for _, event := range unhandledEvents {
		handled := w.Handle(event)
		if handled {
			t.Errorf("event %v got handled", event)
		}
	}
}

func TestHandle_AbbreviationExpansionInterruptedByExternalMutation(t *testing.T) {
	w := &Widget{
		Abbreviations: func(f func(abbr, full string)) {
			f("dn", "/dev/null")
		},
	}
	w.Handle(term.K('d'))
	w.State.MutateCodeBuffer(func(c *CodeBuffer) { c.InsertAtDot("d") })
	w.Handle(term.K('n'))
	wantState := State{CodeBuffer: CodeBuffer{Content: "ddn", Dot: 3}}
	if !reflect.DeepEqual(w.State, wantState) {
		t.Errorf("got state %v, want %v", w.State, wantState)
	}
}

func TestHandle_EnterEmitsSubmit(t *testing.T) {
	var submitted string
	w := &Widget{
		State:    State{CodeBuffer: CodeBuffer{Content: "code", Dot: 4}},
		OnSubmit: func(code string) { submitted = code },
	}
	w.Handle(term.K('\n'))
	if submitted != "code" {
		t.Errorf("code not submitted")
	}
}

func TestHandle_DefaultNoopSubmit(t *testing.T) {
	w := &Widget{State: State{
		CodeBuffer: CodeBuffer{Content: "code", Dot: 4}}}
	w.Handle(term.K('\n'))
	// No panic, we are good
}
<|endoftext|>


<commit_before>package configfile

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/cli/cli/config/credentials"
	"github.com/docker/cli/cli/config/types"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const (
	// This constant is only used for really old config files when the
	// URL wasn't saved as part of the config file and it was just
	// assumed to be this value.
	defaultIndexServer = "https://index.docker.io/v1/"
)

// ConfigFile ~/.docker/config.json file info
type ConfigFile struct {
	AuthConfigs          map[string]types.AuthConfig  `json:"auths"`
	HTTPHeaders          map[string]string            `json:"HttpHeaders,omitempty"`
	PsFormat             string                       `json:"psFormat,omitempty"`
	ImagesFormat         string                       `json:"imagesFormat,omitempty"`
	NetworksFormat       string                       `json:"networksFormat,omitempty"`
	PluginsFormat        string                       `json:"pluginsFormat,omitempty"`
	VolumesFormat        string                       `json:"volumesFormat,omitempty"`
	StatsFormat          string                       `json:"statsFormat,omitempty"`
	DetachKeys           string                       `json:"detachKeys,omitempty"`
	CredentialsStore     string                       `json:"credsStore,omitempty"`
	CredentialHelpers    map[string]string            `json:"credHelpers,omitempty"`
	Filename             string                       `json:"-"` // Note: for internal use only
	ServiceInspectFormat string                       `json:"serviceInspectFormat,omitempty"`
	ServicesFormat       string                       `json:"servicesFormat,omitempty"`
	TasksFormat          string                       `json:"tasksFormat,omitempty"`
	SecretFormat         string                       `json:"secretFormat,omitempty"`
	ConfigFormat         string                       `json:"configFormat,omitempty"`
	NodesFormat          string                       `json:"nodesFormat,omitempty"`
	PruneFilters         []string                     `json:"pruneFilters,omitempty"`
	Proxies              map[string]ProxyConfig       `json:"proxies,omitempty"`
	Experimental         string                       `json:"experimental,omitempty"`
	StackOrchestrator    string                       `json:"stackOrchestrator,omitempty"`
	Kubernetes           *KubernetesConfig            `json:"kubernetes,omitempty"`
	CurrentContext       string                       `json:"currentContext,omitempty"`
	CLIPluginsExtraDirs  []string                     `json:"cliPluginsExtraDirs,omitempty"`
	Plugins              map[string]map[string]string `json:"plugins,omitempty"`
	Aliases              map[string]string            `json:"aliases,omitempty"`
}

// ProxyConfig contains proxy configuration settings
type ProxyConfig struct {
	HTTPProxy  string `json:"httpProxy,omitempty"`
	HTTPSProxy string `json:"httpsProxy,omitempty"`
	NoProxy    string `json:"noProxy,omitempty"`
	FTPProxy   string `json:"ftpProxy,omitempty"`
	AllProxy   string `json:"allProxy,omitempty"`
}

// KubernetesConfig contains Kubernetes orchestrator settings
type KubernetesConfig struct {
	AllNamespaces string `json:"allNamespaces,omitempty"`
}

// New initializes an empty configuration file for the given filename 'fn'
func New(fn string) *ConfigFile {
	return &ConfigFile{
		AuthConfigs: make(map[string]types.AuthConfig),
		HTTPHeaders: make(map[string]string),
		Filename:    fn,
		Plugins:     make(map[string]map[string]string),
		Aliases:     make(map[string]string),
	}
}

// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
// auth config information with given directory and populates the receiver object
func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
	b, err := ioutil.ReadAll(configData)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
		arr := strings.Split(string(b), "\n")
		if len(arr) < 2 {
			return errors.Errorf("The Auth config file is empty")
		}
		authConfig := types.AuthConfig{}
		origAuth := strings.Split(arr[0], " = ")
		if len(origAuth) != 2 {
			return errors.Errorf("Invalid Auth config file")
		}
		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
		if err != nil {
			return err
		}
		authConfig.ServerAddress = defaultIndexServer
		configFile.AuthConfigs[defaultIndexServer] = authConfig
	} else {
		for k, authConfig := range configFile.AuthConfigs {
			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
			if err != nil {
				return err
			}
			authConfig.Auth = ""
			authConfig.ServerAddress = k
			configFile.AuthConfigs[k] = authConfig
		}
	}
	return nil
}

// LoadFromReader reads the configuration data given and sets up the auth config
// information with given directory and populates the receiver object
func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
	if err := json.NewDecoder(configData).Decode(&configFile); err != nil && !errors.Is(err, io.EOF) {
		return err
	}
	var err error
	for addr, ac := range configFile.AuthConfigs {
		if ac.Auth != "" {
			ac.Username, ac.Password, err = decodeAuth(ac.Auth)
			if err != nil {
				return err
			}
		}
		ac.Auth = ""
		ac.ServerAddress = addr
		configFile.AuthConfigs[addr] = ac
	}
	return checkKubernetesConfiguration(configFile.Kubernetes)
}

// ContainsAuth returns whether there is authentication configured
// in this file or not.
func (configFile *ConfigFile) ContainsAuth() bool {
	return configFile.CredentialsStore != "" ||
		len(configFile.CredentialHelpers) > 0 ||
		len(configFile.AuthConfigs) > 0
}

// GetAuthConfigs returns the mapping of repo to auth configuration
func (configFile *ConfigFile) GetAuthConfigs() map[string]types.AuthConfig {
	return configFile.AuthConfigs
}

// SaveToWriter encodes and writes out all the authorization information to
// the given writer
func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
	// Encode sensitive data into a new/temp struct
	tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
	for k, authConfig := range configFile.AuthConfigs {
		authCopy := authConfig
		// encode and save the authstring, while blanking out the original fields
		authCopy.Auth = encodeAuth(&authCopy)
		authCopy.Username = ""
		authCopy.Password = ""
		authCopy.ServerAddress = ""
		tmpAuthConfigs[k] = authCopy
	}

	saveAuthConfigs := configFile.AuthConfigs
	configFile.AuthConfigs = tmpAuthConfigs
	defer func() { configFile.AuthConfigs = saveAuthConfigs }()

	// User-Agent header is automatically set, and should not be stored in the configuration
	for v := range configFile.HTTPHeaders {
		if strings.EqualFold(v, "User-Agent") {
			delete(configFile.HTTPHeaders, v)
		}
	}

	data, err := json.MarshalIndent(configFile, "", "\t")
	if err != nil {
		return err
	}
	_, err = writer.Write(data)
	return err
}

// Save encodes and writes out all the authorization information
func (configFile *ConfigFile) Save() (retErr error) {
	if configFile.Filename == "" {
		return errors.Errorf("Can't save config with empty filename")
	}

	dir := filepath.Dir(configFile.Filename)
	if err := os.MkdirAll(dir, 0700); err != nil {
		return err
	}
	temp, err := ioutil.TempFile(dir, filepath.Base(configFile.Filename))
	if err != nil {
		return err
	}
	defer func() {
		temp.Close()
		if retErr != nil {
			if err := os.Remove(temp.Name()); err != nil {
				logrus.WithError(err).WithField("file", temp.Name()).Debug("Error cleaning up temp file")
			}
		}
	}()

	err = configFile.SaveToWriter(temp)
	if err != nil {
		return err
	}

	if err := temp.Close(); err != nil {
		return errors.Wrap(err, "error closing temp file")
	}

	// Handle situation where the configfile is a symlink
	cfgFile := configFile.Filename
	if f, err := os.Readlink(cfgFile); err == nil {
		cfgFile = f
	}

	// Try copying the current config file (if any) ownership and permissions
	copyFilePermissions(cfgFile, temp.Name())
	return os.Rename(temp.Name(), cfgFile)
}

// ParseProxyConfig computes proxy configuration by retrieving the config for the provided host and
// then checking this against any environment variables provided to the container
func (configFile *ConfigFile) ParseProxyConfig(host string, runOpts map[string]*string) map[string]*string {
	var cfgKey string

	if _, ok := configFile.Proxies[host]; !ok {
		cfgKey = "default"
	} else {
		cfgKey = host
	}

	config := configFile.Proxies[cfgKey]
	permitted := map[string]*string{
		"HTTP_PROXY":  &config.HTTPProxy,
		"HTTPS_PROXY": &config.HTTPSProxy,
		"NO_PROXY":    &config.NoProxy,
		"FTP_PROXY":   &config.FTPProxy,
		"ALL_PROXY":   &config.AllProxy,
	}
	m := runOpts
	if m == nil {
		m = make(map[string]*string)
	}
	for k := range permitted {
		if *permitted[k] == "" {
			continue
		}
		if _, ok := m[k]; !ok {
			m[k] = permitted[k]
		}
		if _, ok := m[strings.ToLower(k)]; !ok {
			m[strings.ToLower(k)] = permitted[k]
		}
	}
	return m
}

// encodeAuth creates a base64 encoded string to containing authorization information
func encodeAuth(authConfig *types.AuthConfig) string {
	if authConfig.Username == "" && authConfig.Password == "" {
		return ""
	}

	authStr := authConfig.Username + ":" + authConfig.Password
	msg := []byte(authStr)
	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
	base64.StdEncoding.Encode(encoded, msg)
	return string(encoded)
}

// decodeAuth decodes a base64 encoded string and returns username and password
func decodeAuth(authStr string) (string, string, error) {
	if authStr == "" {
		return "", "", nil
	}

	decLen := base64.StdEncoding.DecodedLen(len(authStr))
	decoded := make([]byte, decLen)
	authByte := []byte(authStr)
	n, err := base64.StdEncoding.Decode(decoded, authByte)
	if err != nil {
		return "", "", err
	}
	if n > decLen {
		return "", "", errors.Errorf("Something went wrong decoding auth config")
	}
	arr := strings.SplitN(string(decoded), ":", 2)
	if len(arr) != 2 {
		return "", "", errors.Errorf("Invalid auth configuration file")
	}
	password := strings.Trim(arr[1], "\x00")
	return arr[0], password, nil
}

// GetCredentialsStore returns a new credentials store from the settings in the
// configuration file
func (configFile *ConfigFile) GetCredentialsStore(registryHostname string) credentials.Store {
	if helper := getConfiguredCredentialStore(configFile, registryHostname); helper != "" {
		return newNativeStore(configFile, helper)
	}
	return credentials.NewFileStore(configFile)
}

// var for unit testing.
var newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
	return credentials.NewNativeStore(configFile, helperSuffix)
}

// GetAuthConfig for a repository from the credential store
func (configFile *ConfigFile) GetAuthConfig(registryHostname string) (types.AuthConfig, error) {
	return configFile.GetCredentialsStore(registryHostname).Get(registryHostname)
}

// getConfiguredCredentialStore returns the credential helper configured for the
// given registry, the default credsStore, or the empty string if neither are
// configured.
func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string {
	if c.CredentialHelpers != nil && registryHostname != "" {
		if helper, exists := c.CredentialHelpers[registryHostname]; exists {
			return helper
		}
	}
	return c.CredentialsStore
}

// GetAllCredentials returns all of the credentials stored in all of the
// configured credential stores.
func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) {
	auths := make(map[string]types.AuthConfig)
	addAll := func(from map[string]types.AuthConfig) {
		for reg, ac := range from {
			auths[reg] = ac
		}
	}

	defaultStore := configFile.GetCredentialsStore("")
	newAuths, err := defaultStore.GetAll()
	if err != nil {
		return nil, err
	}
	addAll(newAuths)

	// Auth configs from a registry-specific helper should override those from the default store.
	for registryHostname := range configFile.CredentialHelpers {
		newAuth, err := configFile.GetAuthConfig(registryHostname)
		if err != nil {
			return nil, err
		}
		auths[registryHostname] = newAuth
	}
	return auths, nil
}

// GetFilename returns the file name that this config file is based on.
func (configFile *ConfigFile) GetFilename() string {
	return configFile.Filename
}

// PluginConfig retrieves the requested option for the given plugin.
func (configFile *ConfigFile) PluginConfig(pluginname, option string) (string, bool) {
	if configFile.Plugins == nil {
		return "", false
	}
	pluginConfig, ok := configFile.Plugins[pluginname]
	if !ok {
		return "", false
	}
	value, ok := pluginConfig[option]
	return value, ok
}

// SetPluginConfig sets the option to the given value for the given
// plugin. Passing a value of "" will remove the option. If removing
// the final config item for a given plugin then also cleans up the
// overall plugin entry.
func (configFile *ConfigFile) SetPluginConfig(pluginname, option, value string) {
	if configFile.Plugins == nil {
		configFile.Plugins = make(map[string]map[string]string)
	}
	pluginConfig, ok := configFile.Plugins[pluginname]
	if !ok {
		pluginConfig = make(map[string]string)
		configFile.Plugins[pluginname] = pluginConfig
	}
	if value != "" {
		pluginConfig[option] = value
	} else {
		delete(pluginConfig, option)
	}
	if len(pluginConfig) == 0 {
		delete(configFile.Plugins, pluginname)
	}
}

func checkKubernetesConfiguration(kubeConfig *KubernetesConfig) error {
	if kubeConfig == nil {
		return nil
	}
	switch kubeConfig.AllNamespaces {
	case "":
	case "enabled":
	case "disabled":
	default:
		return fmt.Errorf("invalid 'kubernetes.allNamespaces' value, should be 'enabled' or 'disabled': %s", kubeConfig.AllNamespaces)
	}
	return nil
}
<commit_msg>Fix incorrect pointer inputs to `json.Unmarshal`<commit_after>package configfile

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/docker/cli/cli/config/credentials"
	"github.com/docker/cli/cli/config/types"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

const (
	// This constant is only used for really old config files when the
	// URL wasn't saved as part of the config file and it was just
	// assumed to be this value.
	defaultIndexServer = "https://index.docker.io/v1/"
)

// ConfigFile ~/.docker/config.json file info
type ConfigFile struct {
	AuthConfigs          map[string]types.AuthConfig  `json:"auths"`
	HTTPHeaders          map[string]string            `json:"HttpHeaders,omitempty"`
	PsFormat             string                       `json:"psFormat,omitempty"`
	ImagesFormat         string                       `json:"imagesFormat,omitempty"`
	NetworksFormat       string                       `json:"networksFormat,omitempty"`
	PluginsFormat        string                       `json:"pluginsFormat,omitempty"`
	VolumesFormat        string                       `json:"volumesFormat,omitempty"`
	StatsFormat          string                       `json:"statsFormat,omitempty"`
	DetachKeys           string                       `json:"detachKeys,omitempty"`
	CredentialsStore     string                       `json:"credsStore,omitempty"`
	CredentialHelpers    map[string]string            `json:"credHelpers,omitempty"`
	Filename             string                       `json:"-"` // Note: for internal use only
	ServiceInspectFormat string                       `json:"serviceInspectFormat,omitempty"`
	ServicesFormat       string                       `json:"servicesFormat,omitempty"`
	TasksFormat          string                       `json:"tasksFormat,omitempty"`
	SecretFormat         string                       `json:"secretFormat,omitempty"`
	ConfigFormat         string                       `json:"configFormat,omitempty"`
	NodesFormat          string                       `json:"nodesFormat,omitempty"`
	PruneFilters         []string                     `json:"pruneFilters,omitempty"`
	Proxies              map[string]ProxyConfig       `json:"proxies,omitempty"`
	Experimental         string                       `json:"experimental,omitempty"`
	StackOrchestrator    string                       `json:"stackOrchestrator,omitempty"`
	Kubernetes           *KubernetesConfig            `json:"kubernetes,omitempty"`
	CurrentContext       string                       `json:"currentContext,omitempty"`
	CLIPluginsExtraDirs  []string                     `json:"cliPluginsExtraDirs,omitempty"`
	Plugins              map[string]map[string]string `json:"plugins,omitempty"`
	Aliases              map[string]string            `json:"aliases,omitempty"`
}

// ProxyConfig contains proxy configuration settings
type ProxyConfig struct {
	HTTPProxy  string `json:"httpProxy,omitempty"`
	HTTPSProxy string `json:"httpsProxy,omitempty"`
	NoProxy    string `json:"noProxy,omitempty"`
	FTPProxy   string `json:"ftpProxy,omitempty"`
	AllProxy   string `json:"allProxy,omitempty"`
}

// KubernetesConfig contains Kubernetes orchestrator settings
type KubernetesConfig struct {
	AllNamespaces string `json:"allNamespaces,omitempty"`
}

// New initializes an empty configuration file for the given filename 'fn'
func New(fn string) *ConfigFile {
	return &ConfigFile{
		AuthConfigs: make(map[string]types.AuthConfig),
		HTTPHeaders: make(map[string]string),
		Filename:    fn,
		Plugins:     make(map[string]map[string]string),
		Aliases:     make(map[string]string),
	}
}

// LegacyLoadFromReader reads the non-nested configuration data given and sets up the
// auth config information with given directory and populates the receiver object
func (configFile *ConfigFile) LegacyLoadFromReader(configData io.Reader) error {
	b, err := ioutil.ReadAll(configData)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(b, &configFile.AuthConfigs); err != nil {
		arr := strings.Split(string(b), "\n")
		if len(arr) < 2 {
			return errors.Errorf("The Auth config file is empty")
		}
		authConfig := types.AuthConfig{}
		origAuth := strings.Split(arr[0], " = ")
		if len(origAuth) != 2 {
			return errors.Errorf("Invalid Auth config file")
		}
		authConfig.Username, authConfig.Password, err = decodeAuth(origAuth[1])
		if err != nil {
			return err
		}
		authConfig.ServerAddress = defaultIndexServer
		configFile.AuthConfigs[defaultIndexServer] = authConfig
	} else {
		for k, authConfig := range configFile.AuthConfigs {
			authConfig.Username, authConfig.Password, err = decodeAuth(authConfig.Auth)
			if err != nil {
				return err
			}
			authConfig.Auth = ""
			authConfig.ServerAddress = k
			configFile.AuthConfigs[k] = authConfig
		}
	}
	return nil
}

// LoadFromReader reads the configuration data given and sets up the auth config
// information with given directory and populates the receiver object
func (configFile *ConfigFile) LoadFromReader(configData io.Reader) error {
	if err := json.NewDecoder(configData).Decode(configFile); err != nil && !errors.Is(err, io.EOF) {
		return err
	}
	var err error
	for addr, ac := range configFile.AuthConfigs {
		if ac.Auth != "" {
			ac.Username, ac.Password, err = decodeAuth(ac.Auth)
			if err != nil {
				return err
			}
		}
		ac.Auth = ""
		ac.ServerAddress = addr
		configFile.AuthConfigs[addr] = ac
	}
	return checkKubernetesConfiguration(configFile.Kubernetes)
}

// ContainsAuth returns whether there is authentication configured
// in this file or not.
func (configFile *ConfigFile) ContainsAuth() bool {
	return configFile.CredentialsStore != "" ||
		len(configFile.CredentialHelpers) > 0 ||
		len(configFile.AuthConfigs) > 0
}

// GetAuthConfigs returns the mapping of repo to auth configuration
func (configFile *ConfigFile) GetAuthConfigs() map[string]types.AuthConfig {
	return configFile.AuthConfigs
}

// SaveToWriter encodes and writes out all the authorization information to
// the given writer
func (configFile *ConfigFile) SaveToWriter(writer io.Writer) error {
	// Encode sensitive data into a new/temp struct
	tmpAuthConfigs := make(map[string]types.AuthConfig, len(configFile.AuthConfigs))
	for k, authConfig := range configFile.AuthConfigs {
		authCopy := authConfig
		// encode and save the authstring, while blanking out the original fields
		authCopy.Auth = encodeAuth(&authCopy)
		authCopy.Username = ""
		authCopy.Password = ""
		authCopy.ServerAddress = ""
		tmpAuthConfigs[k] = authCopy
	}

	saveAuthConfigs := configFile.AuthConfigs
	configFile.AuthConfigs = tmpAuthConfigs
	defer func() { configFile.AuthConfigs = saveAuthConfigs }()

	// User-Agent header is automatically set, and should not be stored in the configuration
	for v := range configFile.HTTPHeaders {
		if strings.EqualFold(v, "User-Agent") {
			delete(configFile.HTTPHeaders, v)
		}
	}

	data, err := json.MarshalIndent(configFile, "", "\t")
	if err != nil {
		return err
	}
	_, err = writer.Write(data)
	return err
}

// Save encodes and writes out all the authorization information
func (configFile *ConfigFile) Save() (retErr error) {
	if configFile.Filename == "" {
		return errors.Errorf("Can't save config with empty filename")
	}

	dir := filepath.Dir(configFile.Filename)
	if err := os.MkdirAll(dir, 0700); err != nil {
		return err
	}
	temp, err := ioutil.TempFile(dir, filepath.Base(configFile.Filename))
	if err != nil {
		return err
	}
	defer func() {
		temp.Close()
		if retErr != nil {
			if err := os.Remove(temp.Name()); err != nil {
				logrus.WithError(err).WithField("file", temp.Name()).Debug("Error cleaning up temp file")
			}
		}
	}()

	err = configFile.SaveToWriter(temp)
	if err != nil {
		return err
	}

	if err := temp.Close(); err != nil {
		return errors.Wrap(err, "error closing temp file")
	}

	// Handle situation where the configfile is a symlink
	cfgFile := configFile.Filename
	if f, err := os.Readlink(cfgFile); err == nil {
		cfgFile = f
	}

	// Try copying the current config file (if any) ownership and permissions
	copyFilePermissions(cfgFile, temp.Name())
	return os.Rename(temp.Name(), cfgFile)
}

// ParseProxyConfig computes proxy configuration by retrieving the config for the provided host and
// then checking this against any environment variables provided to the container
func (configFile *ConfigFile) ParseProxyConfig(host string, runOpts map[string]*string) map[string]*string {
	var cfgKey string

	if _, ok := configFile.Proxies[host]; !ok {
		cfgKey = "default"
	} else {
		cfgKey = host
	}

	config := configFile.Proxies[cfgKey]
	permitted := map[string]*string{
		"HTTP_PROXY":  &config.HTTPProxy,
		"HTTPS_PROXY": &config.HTTPSProxy,
		"NO_PROXY":    &config.NoProxy,
		"FTP_PROXY":   &config.FTPProxy,
		"ALL_PROXY":   &config.AllProxy,
	}
	m := runOpts
	if m == nil {
		m = make(map[string]*string)
	}
	for k := range permitted {
		if *permitted[k] == "" {
			continue
		}
		if _, ok := m[k]; !ok {
			m[k] = permitted[k]
		}
		if _, ok := m[strings.ToLower(k)]; !ok {
			m[strings.ToLower(k)] = permitted[k]
		}
	}
	return m
}

// encodeAuth creates a base64 encoded string to containing authorization information
func encodeAuth(authConfig *types.AuthConfig) string {
	if authConfig.Username == "" && authConfig.Password == "" {
		return ""
	}

	authStr := authConfig.Username + ":" + authConfig.Password
	msg := []byte(authStr)
	encoded := make([]byte, base64.StdEncoding.EncodedLen(len(msg)))
	base64.StdEncoding.Encode(encoded, msg)
	return string(encoded)
}

// decodeAuth decodes a base64 encoded string and returns username and password
func decodeAuth(authStr string) (string, string, error) {
	if authStr == "" {
		return "", "", nil
	}

	decLen := base64.StdEncoding.DecodedLen(len(authStr))
	decoded := make([]byte, decLen)
	authByte := []byte(authStr)
	n, err := base64.StdEncoding.Decode(decoded, authByte)
	if err != nil {
		return "", "", err
	}
	if n > decLen {
		return "", "", errors.Errorf("Something went wrong decoding auth config")
	}
	arr := strings.SplitN(string(decoded), ":", 2)
	if len(arr) != 2 {
		return "", "", errors.Errorf("Invalid auth configuration file")
	}
	password := strings.Trim(arr[1], "\x00")
	return arr[0], password, nil
}

// GetCredentialsStore returns a new credentials store from the settings in the
// configuration file
func (configFile *ConfigFile) GetCredentialsStore(registryHostname string) credentials.Store {
	if helper := getConfiguredCredentialStore(configFile, registryHostname); helper != "" {
		return newNativeStore(configFile, helper)
	}
	return credentials.NewFileStore(configFile)
}

// var for unit testing.
var newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
	return credentials.NewNativeStore(configFile, helperSuffix)
}

// GetAuthConfig for a repository from the credential store
func (configFile *ConfigFile) GetAuthConfig(registryHostname string) (types.AuthConfig, error) {
	return configFile.GetCredentialsStore(registryHostname).Get(registryHostname)
}

// getConfiguredCredentialStore returns the credential helper configured for the
// given registry, the default credsStore, or the empty string if neither are
// configured.
func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string {
	if c.CredentialHelpers != nil && registryHostname != "" {
		if helper, exists := c.CredentialHelpers[registryHostname]; exists {
			return helper
		}
	}
	return c.CredentialsStore
}

// GetAllCredentials returns all of the credentials stored in all of the
// configured credential stores.
func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) {
	auths := make(map[string]types.AuthConfig)
	addAll := func(from map[string]types.AuthConfig) {
		for reg, ac := range from {
			auths[reg] = ac
		}
	}

	defaultStore := configFile.GetCredentialsStore("")
	newAuths, err := defaultStore.GetAll()
	if err != nil {
		return nil, err
	}
	addAll(newAuths)

	// Auth configs from a registry-specific helper should override those from the default store.
	for registryHostname := range configFile.CredentialHelpers {
		newAuth, err := configFile.GetAuthConfig(registryHostname)
		if err != nil {
			return nil, err
		}
		auths[registryHostname] = newAuth
	}
	return auths, nil
}

// GetFilename returns the file name that this config file is based on.
func (configFile *ConfigFile) GetFilename() string {
	return configFile.Filename
}

// PluginConfig retrieves the requested option for the given plugin.
func (configFile *ConfigFile) PluginConfig(pluginname, option string) (string, bool) {
	if configFile.Plugins == nil {
		return "", false
	}
	pluginConfig, ok := configFile.Plugins[pluginname]
	if !ok {
		return "", false
	}
	value, ok := pluginConfig[option]
	return value, ok
}

// SetPluginConfig sets the option to the given value for the given
// plugin. Passing a value of "" will remove the option. If removing
// the final config item for a given plugin then also cleans up the
// overall plugin entry.
func (configFile *ConfigFile) SetPluginConfig(pluginname, option, value string) {
	if configFile.Plugins == nil {
		configFile.Plugins = make(map[string]map[string]string)
	}
	pluginConfig, ok := configFile.Plugins[pluginname]
	if !ok {
		pluginConfig = make(map[string]string)
		configFile.Plugins[pluginname] = pluginConfig
	}
	if value != "" {
		pluginConfig[option] = value
	} else {
		delete(pluginConfig, option)
	}
	if len(pluginConfig) == 0 {
		delete(configFile.Plugins, pluginname)
	}
}

func checkKubernetesConfiguration(kubeConfig *KubernetesConfig) error {
	if kubeConfig == nil {
		return nil
	}
	switch kubeConfig.AllNamespaces {
	case "":
	case "enabled":
	case "disabled":
	default:
		return fmt.Errorf("invalid 'kubernetes.allNamespaces' value, should be 'enabled' or 'disabled': %s", kubeConfig.AllNamespaces)
	}
	return nil
}
<|endoftext|>