<commit_before>package main

import (
    "bufio"
    "container/list"
    "errors"
    "fmt"
    "os"
    "regexp"
    "strconv"
    "strings"
)

type Editor struct {
    buffer *list.List
    filename string
    line int
    modified bool
    err string
    commands map[rune]func(int, int, rune, string)
	pattern *regexp.Regexp
}

func NewEditor() *Editor {
    e := &Editor{}
    e.buffer = list.New()
    e.line = 1
	e.pattern = nil

    e.commands = map[rune]func(int, int, rune, string){
        'p': e.Print,
        'n': e.Print,
        'i': e.Insert,
        'a': e.Insert,
        'd': e.Delete,
        'c': e.Change,
        'e': e.OpenWrapper,
        'E': e.OpenWrapper,
        's': e.ReSub,
        'w': e.Write,
        'h': e.Help,
        'q': e.Quit,
        'Q': e.Quit,
    }

    return e
}

func (e *Editor) isModified() bool {
    if e.modified {
        e.Error("warning: file modified")
        e.modified = false
        return true
    } else {
        return false
    }
}

func (e *Editor) Index(idx int) *list.Element {
    for i, l := 0, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i == idx {
            return l
        }
    }

    return nil
}

func (e *Editor) LastAddr() int {
	return e.buffer.Len()
}

func (e *Editor) CurrentAddr() int {
	return e.line
}

// Search uses Editor.pattern for matching
func (e *Editor) Search(fwd bool) (num int, err error) {
	// helper
	cyclicNextElem := func (el *list.Element, l *list.List, i int, fwd bool) (e *list.Element, idx int) {
		idx = i
		if fwd {
			e = el.Next()
			idx++
			if e == nil {
				e = l.Front()
				idx = 1
			}
		} else {
			e = el.Prev()
			idx--
			if e == nil {
				e = l.Back()
				idx = l.Len()
			}
		}
		return
	}

	// check previous regexp pattern
	if e.pattern == nil {
		return InvalidAddr, errors.New("no previous pattern")
	}

	rx := e.pattern
	num = e.line

	start := e.Index(num-1)
	if start == nil {
		return InvalidAddr, errors.New("wrong line number")
	}

	// Start searching from this line
	fromLine := start

	// we change fromLine because we do not match the line we start at
	fromLine, num = cyclicNextElem(start, e.buffer, num, fwd)

	current := fromLine
	for {
		value := current.Value.(string)
		// check for match
		if rx.MatchString(value) {
			return num, nil
		}

		// if no match get next line
		current, num = cyclicNextElem(current, e.buffer, num, fwd)

		// we have wrapped around without matching
		if current == start {
			return InvalidAddr, ErrNoMatch
		}
	} // for
}

func (e *Editor) OpenWrapper(start, end int, cmd rune, text string) {
    args := strings.Split(text, " ")
    filename := ""

    if len(args) == 1 {
        return
    }

    if cmd != 'E' {
        if e.isModified() {
            return
        }
    }

    filename = args[1]
    e.Open(filename)
}

func (e *Editor) Open(filename string) {
    file, err := os.Open(filename)
    defer file.Close()

    if err != nil {
        fmt.Println(err)
        e.Error("cannot open input file")
        return
    }

    e.buffer = list.New()
    e.filename = filename
    e.modified = false
    size := 0

    scanner := bufio.NewScanner(file)

    for i := 1; scanner.Scan(); i++ {
        text := scanner.Text()
        size += len(text) + 1
        e.buffer.PushBack(text)

        e.line = i
    }

    fmt.Println(size)
}

func (e *Editor) Write(start, end int, cmd rune, text string) {
    args := strings.Split(text, " ")

    if len(args) > 1 {
        e.filename = args[1]
    }

    if len(e.filename) == 0 {
        e.Error("no current filename")
        return
    }

    file, err := os.Create(e.filename)
    defer file.Close()

    if err != nil {
        fmt.Println(err)
        e.Error("cannot write to file")
        return
    }

    size := 0

    for l := e.buffer.Front(); l != nil; l = l.Next() {
        text := l.Value.(string)
        count, _ := file.WriteString(text + "\n")
        size += count
    }

    e.modified = false
    fmt.Println(size)
}

func (e *Editor) Print(start, end int, cmd rune, text string) {
    for i, l := 1, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i >= start && i <= end {
            if cmd == 'n' {
                fmt.Printf("%d\t%s\n", i, l.Value)
            } else {
                fmt.Println(l.Value)
            }

            e.line = i
        }
    }
}

func readLine() string {
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    return scanner.Text()
}

func readLines() *list.List {
    input := list.New()

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        text := scanner.Text()

        if text == "." {
            break
        }

        input.PushBack(text)
    }

    return input
}

func (e *Editor) InsertBefore(other *list.List, line int) {
    node := e.Index(line-1)

    for i, l := other.Len(), other.Back(); i > 0; i, l = i-1, l.Prev() {
        e.buffer.InsertBefore(l.Value, node)
        node = node.Prev()
    }

    e.setLine(e.line + other.Len() - 1)
}

func (e *Editor) InsertAfter(other *list.List, line int) {
    node := e.Index(line-1)

    for i, l := 0, other.Front(); i < other.Len(); i, l = i+1, l.Next() {
        e.buffer.InsertAfter(l.Value, node)
        node = node.Next()
        e.setLine(e.line+1)
    }

    e.setLine(other.Len() + e.line)
}

func (e *Editor) Insert(start, end int, cmd rune, text string) {
    input := readLines()
    e.setLine(end)

    if e.buffer.Len() == 0 {
        e.buffer.PushBackList(input)
        e.setLine(e.line + input.Len())
    } else {
        if cmd == 'i' {
            // edge case
            if end >= e.buffer.Len() {
                e.buffer.PushBackList(input)
                e.setLine(e.line + input.Len())
            } else {
                e.InsertBefore(input, end)
            }
        } else {
            e.InsertAfter(input, end)
        }
    }

    e.modified = true
}

func (e *Editor) setLine(line int) {
    if line > e.buffer.Len() {
        e.line = e.buffer.Len()
    } else if line <= 0 {
        e.line = 1
    } else {
        e.line = line
    }
}

func (e *Editor) Delete(start, end int, cmd rune, text string) {
    curr := e.Index(start-1)

    for i := start; i <= end; i++ {
        next := curr.Next()
        e.buffer.Remove(curr)
        curr = next
    }

    e.setLine(start)
    e.modified = true
}

func (e *Editor) Change(start, end int, cmd rune, text string) {
    e.Delete(start, end, cmd, text)
    e.Insert(start, end, 'i', text)
}

func (e *Editor) Error(msg string) {
    e.err = msg
    fmt.Println("?")
}

func (e *Editor) replaceMacros(text string) string {
    macros := map[string]int{
        ".": e.line,
        "+": e.line+1,
        "-": e.line-1,
        "$": e.buffer.Len(),
    }

    for key, value := range macros {
        text = strings.Replace(text, key, strconv.Itoa(value), -1)
    }

    return text
}

func (e *Editor) ReSub(start, end int, cmd rune, text string) {
	sep := "/"
	if len(text) > 1 {
		sep = string(text[1])
	}
	if sep == " " || sep == "\t" || sep == "\n" || sep == "\r" {
		e.Error("invalid pattern delimiter")
		return
	}
	parts := strings.Split(text, sep)
	lenparts := len(parts)

	if lenparts < 2 || lenparts > 4 { // not taking single 's' into account
		e.Error("invalid command syntax")
		return
	}

    match := parts[1]
	replace := ""
	if lenparts > 2 {
		replace = parts[2]
	}
	flags := ""
	if lenparts > 3 {
		flags = parts[3]
	}

    if strings.ContainsRune(flags, 'i') {
        match = "(?i)" + match
    }

	global := false
	if strings.ContainsRune(flags, 'g') {
		global = true
	}

    re, err := regexp.Compile(match)

    if err != nil {
        e.Error("invalid regexp")
        return
    }

    for i, l := 1, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
		if i > end {
			break
		}
		if i >= start && i <= end {
			line := l.Value.(string)
			if global {
				l.Value = re.ReplaceAllString(line, replace)
			} else {
				pos := re.FindStringIndex(line)
				if pos != nil {
					head := line[:pos[1]]
					tail := line[pos[1]:]
					l.Value = re.ReplaceAllString(head, replace) + tail
				}
			}

            e.line = i
        }
    }

	if lenparts == 3 || strings.ContainsRune(flags, 'p') { // 'p' flag by default
		e.Print(end, end, 'p', text)
	}

    e.modified = true
}

func (e *Editor) Quit(start, end int, cmd rune, text string) {
    if cmd == 'Q' || !e.isModified() {
        os.Exit(0)
    }
}

func (e *Editor) Help(start, end int, cmd rune, text string) {
    if len(e.err) > 0 {
        fmt.Println(e.err)
    }
}

func (e *Editor) Parse(text string) (int, int, string) {
    if len(text) == 0 {
        return e.line+1, e.line+1, "p"
    }

    index := -1
    for i, c := range text {
        if _, ok := e.commands[c]; ok {
            index = i
            break
        }
    }

    if index == 0 {
        return e.line, e.line, text
    }

    var nrange, rest string

    if index == -1 {
        nrange = text
    } else {
        nrange = text[:index]
    }

    nrange = e.replaceMacros(nrange)

    nums := strings.Split(nrange, ",")
    start := 0
    end := 0

    if nrange == "," || nrange == "%" {
        start = 1
        end = e.buffer.Len()
    } else if len(nums) == 2 {
        start, _ = strconv.Atoi(nums[0])
        end, _ = strconv.Atoi(nums[1])
    } else if len(nums) == 1 {
        start, _ = strconv.Atoi(nums[0])
        end = start
    }

    if start == 0 && end == 0 {
        // Invalid input
    } else if index == -1 {
        rest = "p"
    } else {
        rest = text[index:]
    }

    return start, end, rest
}

func (e *Editor) Prompt() {
    text := readLine()
	p := NewLineParser(e, text)

	start, end, cmd, text := p.Parse()

    if text == "" {
        e.Error("unknown command")
        return
    }

    // Special check when working on an empty buffer
    if (e.buffer.Len() != 0 && start != 1) &&
           (start < 1 || end > e.buffer.Len() ||
           start > end) {
        e.Error("invalid address")
        return
    }

    if fn, ok := e.commands[cmd]; ok {
        fn(start, end, cmd, text)
    } else {
        e.Error("unknown command")
    }
}

func main() {
    editor := NewEditor()

    if len(os.Args) > 1 {
        editor.Open(os.Args[1])
    }

    for {
        editor.Prompt()
    }
}
<commit_msg>FIX: d command panics on empty buffer, checks for empty buffer<commit_after>package main

import (
    "bufio"
    "container/list"
    "errors"
    "fmt"
    "os"
    "regexp"
    "strconv"
    "strings"
)

type Editor struct {
    buffer *list.List
    filename string
    line int
    modified bool
    err string
    commands map[rune]func(int, int, rune, string)
	pattern *regexp.Regexp
}

func NewEditor() *Editor {
    e := &Editor{}
    e.buffer = list.New()
    e.line = 0
	e.pattern = nil

    e.commands = map[rune]func(int, int, rune, string){
        'p': e.Print,
        'n': e.Print,
        'i': e.Insert,
        'a': e.Insert,
        'd': e.Delete,
        'c': e.Change,
        'e': e.OpenWrapper,
        'E': e.OpenWrapper,
        's': e.ReSub,
        'w': e.Write,
        'h': e.Help,
        'q': e.Quit,
        'Q': e.Quit,
    }

    return e
}

func (e *Editor) isModified() bool {
    if e.modified {
        e.Error("warning: file modified")
        e.modified = false
        return true
    } else {
        return false
    }
}

func (e *Editor) Index(idx int) *list.Element {
    for i, l := 0, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i == idx {
            return l
        }
    }

    return nil
}

func (e *Editor) LastAddr() int {
	return e.buffer.Len()
}

func (e *Editor) CurrentAddr() int {
	return e.line
}

func (e *Editor) IsBufferEmpty() bool {
	return e.buffer.Len() <= 0
}

// Search uses Editor.pattern for matching
func (e *Editor) Search(fwd bool) (num int, err error) {
	// helper
	cyclicNextElem := func (el *list.Element, l *list.List, i int, fwd bool) (e *list.Element, idx int) {
		idx = i
		if fwd {
			e = el.Next()
			idx++
			if e == nil {
				e = l.Front()
				idx = 1
			}
		} else {
			e = el.Prev()
			idx--
			if e == nil {
				e = l.Back()
				idx = l.Len()
			}
		}
		return
	}

	// check previous regexp pattern
	if e.pattern == nil {
		return InvalidAddr, errors.New("no previous pattern")
	}

	rx := e.pattern
	num = e.line

	start := e.Index(num-1)
	if start == nil {
		return InvalidAddr, errors.New("wrong line number")
	}

	// Start searching from this line
	fromLine := start

	// we change fromLine because we do not match the line we start at
	fromLine, num = cyclicNextElem(start, e.buffer, num, fwd)

	current := fromLine
	for {
		value := current.Value.(string)
		// check for match
		if rx.MatchString(value) {
			return num, nil
		}

		// if no match get next line
		current, num = cyclicNextElem(current, e.buffer, num, fwd)

		// we have wrapped around without matching
		if current == start {
			return InvalidAddr, ErrNoMatch
		}
	} // for
}

func (e *Editor) OpenWrapper(start, end int, cmd rune, text string) {
    args := strings.Split(text, " ")
    filename := ""

    if len(args) == 1 {
        return
    }

    if cmd != 'E' {
        if e.isModified() {
            return
        }
    }

    filename = args[1]
    e.Open(filename)
}

func (e *Editor) Open(filename string) {
    file, err := os.Open(filename)
    defer file.Close()

    if err != nil {
        fmt.Println(err)
        e.Error("cannot open input file")
        return
    }

    e.buffer = list.New()
    e.filename = filename
    e.modified = false
    size := 0

    scanner := bufio.NewScanner(file)

    for i := 1; scanner.Scan(); i++ {
        text := scanner.Text()
        size += len(text) + 1
        e.buffer.PushBack(text)

        e.line = i
    }

    fmt.Println(size)
}

func (e *Editor) Write(start, end int, cmd rune, text string) {
    args := strings.Split(text, " ")

    if len(args) > 1 {
        e.filename = args[1]
    }

    if len(e.filename) == 0 {
        e.Error("no current filename")
        return
    }

    file, err := os.Create(e.filename)
    defer file.Close()

    if err != nil {
        fmt.Println(err)
        e.Error("cannot write to file")
        return
    }

    size := 0

    for l := e.buffer.Front(); l != nil; l = l.Next() {
        text := l.Value.(string)
        count, _ := file.WriteString(text + "\n")
        size += count
    }

    e.modified = false
    fmt.Println(size)
}

func (e *Editor) Print(start, end int, cmd rune, text string) {
	if e.IsBufferEmpty() {
		e.Error("invalid address")
		return
	}
    for i, l := 1, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
        if i >= start && i <= end {
            if cmd == 'n' {
                fmt.Printf("%d\t%s\n", i, l.Value)
            } else {
                fmt.Println(l.Value)
            }

            e.line = i
        }
    }
}

func readLine() string {
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Scan()
    return scanner.Text()
}

func readLines() *list.List {
    input := list.New()

    scanner := bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        text := scanner.Text()

        if text == "." {
            break
        }

        input.PushBack(text)
    }

    return input
}

func (e *Editor) InsertBefore(other *list.List, line int) {
    node := e.Index(line-1)

    for i, l := other.Len(), other.Back(); i > 0; i, l = i-1, l.Prev() {
        e.buffer.InsertBefore(l.Value, node)
        node = node.Prev()
    }

    e.setLine(e.line + other.Len() - 1)
}

func (e *Editor) InsertAfter(other *list.List, line int) {
    node := e.Index(line-1)

    for i, l := 0, other.Front(); i < other.Len(); i, l = i+1, l.Next() {
        e.buffer.InsertAfter(l.Value, node)
        node = node.Next()
        e.setLine(e.line+1)
    }

    e.setLine(other.Len() + e.line)
}

func (e *Editor) Insert(start, end int, cmd rune, text string) {
    input := readLines()
    e.setLine(end)

    if e.buffer.Len() == 0 {
        e.buffer.PushBackList(input)
        e.setLine(e.line + input.Len())
    } else {
        if cmd == 'i' {
            // edge case
            if end >= e.buffer.Len() {
                e.buffer.PushBackList(input)
                e.setLine(e.line + input.Len())
            } else {
                e.InsertBefore(input, end)
            }
        } else {
            e.InsertAfter(input, end)
        }
    }

    e.modified = true
}

func (e *Editor) setLine(line int) {
    if line > e.buffer.Len() {
        e.line = e.buffer.Len()
    } else if line <= 0 {
        e.line = 1
    } else {
        e.line = line
    }
}

func (e *Editor) Delete(start, end int, cmd rune, text string) {
	if e.IsBufferEmpty() {
		e.Error("invalid address")
		return
	}
    curr := e.Index(start-1)
	if curr == nil {
		e.Error("invalid address")
		return
	}

    for i := start; i <= end; i++ {
        next := curr.Next()
        e.buffer.Remove(curr)
        curr = next
    }

    e.setLine(start)
    e.modified = true
}

func (e *Editor) Change(start, end int, cmd rune, text string) {
	if e.IsBufferEmpty() {
		e.Error("invalid address")
		return
	}
    e.Delete(start, end, cmd, text)
    e.Insert(start, end, 'i', text)
}

func (e *Editor) Error(msg string) {
    e.err = msg
    fmt.Println("?")
}

func (e *Editor) replaceMacros(text string) string {
    macros := map[string]int{
        ".": e.line,
        "+": e.line+1,
        "-": e.line-1,
        "$": e.buffer.Len(),
    }

    for key, value := range macros {
        text = strings.Replace(text, key, strconv.Itoa(value), -1)
    }

    return text
}

func (e *Editor) ReSub(start, end int, cmd rune, text string) {
	sep := "/"
	if len(text) > 1 {
		sep = string(text[1])
	}
	if sep == " " || sep == "\t" || sep == "\n" || sep == "\r" {
		e.Error("invalid pattern delimiter")
		return
	}
	parts := strings.Split(text, sep)
	lenparts := len(parts)

	if lenparts < 2 || lenparts > 4 { // not taking single 's' into account
		e.Error("invalid command syntax")
		return
	}

    match := parts[1]
	replace := ""
	if lenparts > 2 {
		replace = parts[2]
	}
	flags := ""
	if lenparts > 3 {
		flags = parts[3]
	}

    if strings.ContainsRune(flags, 'i') {
        match = "(?i)" + match
    }

	global := false
	if strings.ContainsRune(flags, 'g') {
		global = true
	}

    re, err := regexp.Compile(match)

    if err != nil {
        e.Error("invalid regexp")
        return
    }

    for i, l := 1, e.buffer.Front(); l != nil; i, l = i+1, l.Next() {
		if i > end {
			break
		}
		if i >= start && i <= end {
			line := l.Value.(string)
			if global {
				l.Value = re.ReplaceAllString(line, replace)
			} else {
				pos := re.FindStringIndex(line)
				if pos != nil {
					head := line[:pos[1]]
					tail := line[pos[1]:]
					l.Value = re.ReplaceAllString(head, replace) + tail
				}
			}

            e.line = i
        }
    }

	if lenparts == 3 || strings.ContainsRune(flags, 'p') { // 'p' flag by default
		e.Print(end, end, 'p', text)
	}

    e.modified = true
}

func (e *Editor) Quit(start, end int, cmd rune, text string) {
    if cmd == 'Q' || !e.isModified() {
        os.Exit(0)
    }
}

func (e *Editor) Help(start, end int, cmd rune, text string) {
    if len(e.err) > 0 {
        fmt.Println(e.err)
    }
}

func (e *Editor) Parse(text string) (int, int, string) {
    if len(text) == 0 {
        return e.line+1, e.line+1, "p"
    }

    index := -1
    for i, c := range text {
        if _, ok := e.commands[c]; ok {
            index = i
            break
        }
    }

    if index == 0 {
        return e.line, e.line, text
    }

    var nrange, rest string

    if index == -1 {
        nrange = text
    } else {
        nrange = text[:index]
    }

    nrange = e.replaceMacros(nrange)

    nums := strings.Split(nrange, ",")
    start := 0
    end := 0

    if nrange == "," || nrange == "%" {
        start = 1
        end = e.buffer.Len()
    } else if len(nums) == 2 {
        start, _ = strconv.Atoi(nums[0])
        end, _ = strconv.Atoi(nums[1])
    } else if len(nums) == 1 {
        start, _ = strconv.Atoi(nums[0])
        end = start
    }

    if start == 0 && end == 0 {
        // Invalid input
    } else if index == -1 {
        rest = "p"
    } else {
        rest = text[index:]
    }

    return start, end, rest
}

func (e *Editor) Prompt() {
    text := readLine()
	p := NewLineParser(e, text)

	start, end, cmd, text := p.Parse()

    if text == "" {
        e.Error("unknown command")
        return
    }

    // Special check when working on an empty buffer
    if (e.buffer.Len() != 0 && start != 1) &&
           (start < 1 || end > e.buffer.Len() ||
           start > end) {
        e.Error("invalid address")
        return
    }

    if fn, ok := e.commands[cmd]; ok {
        fn(start, end, cmd, text)
    } else {
        e.Error("unknown command")
    }
}

func main() {
    editor := NewEditor()

    if len(os.Args) > 1 {
        editor.Open(os.Args[1])
    }

    for {
        editor.Prompt()
    }
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"time"

	log "github.com/Sirupsen/logrus"
	uuid "github.com/satori/go.uuid"
)

const ID_HEADER = "X-Request-Id"

// Decorates request/response with unique ID
func MdwId(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		id := uuid.NewV4().String()
		req.Header.Set(ID_HEADER, id)
		next.ServeHTTP(w, req)
	})
}

// Logs incoming requests and outgoing responses
func MdwLog(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		go log.WithFields(log.Fields{
			"Id":         req.Header.Get(ID_HEADER),
			"Host":       req.Host,
			"Method":     req.Method,
			"RequestURI": req.RequestURI}).Info("Request")

		start := time.Now()
		next.ServeHTTP(w, req)
		finish := time.Since(start)

		go log.WithFields(log.Fields{
			"Id":       req.Header.Get(ID_HEADER),
			"Duration": finish}).Info("Response")
	})
}
<commit_msg>Child logging<commit_after>package main

import (
	"net/http"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/context"
	uuid "github.com/satori/go.uuid"
)

const ID_HEADER = "X-Request-Id"

// Decorates request/response with unique ID
func MdwId(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		id := uuid.NewV4().String()
		req.Header.Set(ID_HEADER, id)
		next.ServeHTTP(w, req)
	})
}

// Logs incoming requests and outgoing responses. Also provides a child logger to each request's context.
func MdwLog(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		l := log.WithFields(log.Fields{"Id": req.Header.Get(ID_HEADER)})
		context.Set(req, l, "log")

		go l.WithFields(log.Fields{
			"Host":       req.Host,
			"Method":     req.Method,
			"RequestURI": req.RequestURI}).Info("Request")

		start := time.Now()
		next.ServeHTTP(w, req)
		finish := time.Since(start)

		go l.WithFields(log.Fields{"Duration": finish}).Info("Response")
	})
}
<|endoftext|>


<commit_before>// © 2012 the Minima Authors under the MIT license. See AUTHORS for the list of authors.

package main

import (
	"bufio"
	"code.google.com/p/min-game/ai"
	"code.google.com/p/min-game/animal"
	"code.google.com/p/min-game/geom"
	"code.google.com/p/min-game/item"
	"code.google.com/p/min-game/ui"
	"code.google.com/p/min-game/world"
	"encoding/json"
	"io"
	"math/rand"
)

var TileSize = world.TileSize

type Game struct {
	wo         *world.World
	cam        ui.Camera
	base       Base
	Astro      *Player
	Herbivores []animal.Herbivores
	Treasure   []item.Treasure
}

// ReadGame returns a *Game, read from the given
// reader.
func ReadGame(r io.Reader) (*Game, error) {
	g := new(Game)
	in := bufio.NewReader(r)
	var err error
	if g.wo, err = world.Read(in); err != nil {
		return nil, err
	}
	g.cam = ui.Camera{Torus: g.wo.Pixels, Dims: ScreenDims}
	crashSite := geom.Pt(float64(g.wo.X0), float64(g.wo.Y0)).Mul(TileSize)
	g.Astro = NewPlayer(g.wo, crashSite)
	g.base = NewBase(crashSite)

	if err := json.NewDecoder(in).Decode(&g); err != nil {
		panic(err)
	}
	g.CenterOnTile(g.wo.Tile(g.Astro.body.Center()))
	return g, nil
}

func (e *Game) Transparent() bool {
	return false
}

// CenterOnTile centers the display on a given tile.
func (e *Game) CenterOnTile(x, y int) {
	pt := geom.Pt(float64(x), float64(y))
	halfTile := TileSize.Div(geom.Pt(2, 2))
	e.cam.Center(pt.Mul(TileSize).Add(halfTile))
}

func (e *Game) Draw(d ui.Drawer) {
	pt := ScreenDims.Div(TileSize)
	w, h := int(pt.X), int(pt.Y)
	x0, y0 := e.wo.Tile(e.cam.Pt)

	for x := x0; x <= x0+w; x++ {
		for y := y0; y <= y0+h; y++ {
			l := e.wo.At(x, y)
			e.cam.Draw(d, ui.Sprite{
				Name:   l.Terrain.Name,
				Bounds: geom.Rectangle{geom.Pt(0, 0), TileSize},
				Shade:  shade(l),
			}, geom.Pt(float64(x), float64(y)).Mul(TileSize))
		}
	}

	e.base.Draw(d, e.cam)
	for _, t := range e.Treasure {
		if t.Item == nil {
			continue
		}
		e.cam.Draw(d, ui.Sprite{
			Name:   "Present",
			Bounds: geom.Rect(0, 0, t.Box.Dx(), t.Box.Dy()),
			Shade:  shade(e.wo.At(e.wo.Tile(t.Box.Center()))),
		}, t.Box.Min)
	}
	e.Astro.Draw(d, e.cam)
	for i := range e.Herbivores {
		e.Herbivores[i].Draw(d, e.cam)
	}

	e.Astro.drawO2(d)

	if !*debug {
		return
	}
	d.SetFont(DialogFont, 8)
	d.SetColor(White)
	sz := d.TextSize(e.Astro.info)
	d.Draw(e.Astro.info, geom.Pt(0, ScreenDims.Y-sz.Y))
}

// Shade returns the shade value for a location.
func shade(l *world.Loc) float32 {
	const minSh = 0.15
	const slope = (1 - minSh) / world.MaxElevation
	return slope*float32(l.Elevation-l.Depth) + minSh
}

func (ex *Game) Handle(stk *ui.ScreenStack, ev ui.Event) error {
	k, ok := ev.(ui.Key)
	if !ok || !k.Down {
		return nil
	}

	switch k.Button {
	case ui.Menu:
		stk.Push(NewPauseScreen(ex.Astro))
	case ui.Action:
		for i, t := range ex.Treasure {
			if t.Item == nil || !ex.wo.Pixels.Overlaps(ex.Astro.body.Box, t.Box) {
				continue
			}
			scr := NewNormalMessage("You don't have room for that in your pack.")
			if ex.Astro.PutPack(t.Item) {
				scr = NewNormalMessage("Bravo! You got the " + t.Item.Name + "!")
				ex.Treasure[i].Item = nil
			}
			stk.Push(scr)
			return nil
		}
		if ex.wo.Pixels.Overlaps(ex.Astro.body.Box, ex.base.Box) {
			stk.Push(NewBaseScreen(ex.Astro, &ex.base))
			return nil
		}
	case ui.Drop:
		if ex.Astro.Held == nil {
			break
		}
		dropped := ex.Astro.Held
		ex.Astro.Held = nil
		//TODO: we do this a lot…
		i := 0
		for ; i < len(ex.Treasure); i++ {
			if ex.Treasure[i].Item == nil {
				ex.Treasure[i].Item = dropped
				ex.Treasure[i].Box = ex.Astro.body.Box
				break
			}
		}
		if i == len(ex.Treasure) {
			ex.Treasure = append(ex.Treasure, item.Treasure{dropped, ex.Astro.body.Box})
		}
	}
	return nil
}

func (e *Game) Update(stk *ui.ScreenStack) error {
	const speed = 4 // px

	if e.Astro.o2 == 0 && !*debug {
		if et := e.Astro.FindEtele(); et == nil {
			stk.Push(NewGameOverScreen())
		} else {
			et.Uses--
			e.Astro.body.Vel = geom.Pt(0, 0)
			dims := geom.Pt(e.Astro.body.Box.Dx(), e.Astro.body.Box.Dy())
			e.Astro.body.Box.Min = e.base.Box.Min
			e.Astro.body.Box.Max = e.base.Box.Min.Add(dims)
			e.Astro.RefillO2()
		}
	}

	e.Astro.body.Vel = geom.Pt(0, 0)
	if stk.Buttons&ui.Left != 0 {
		e.Astro.body.Vel.X -= speed
	}
	if stk.Buttons&ui.Right != 0 {
		e.Astro.body.Vel.X += speed
	}
	if stk.Buttons&ui.Down != 0 {
		e.Astro.body.Vel.Y += speed
	}
	if stk.Buttons&ui.Up != 0 {
		e.Astro.body.Vel.Y -= speed
	}
	e.Astro.Move(e.wo)
	e.cam.Center(e.Astro.body.Box.Center())

	for i := range e.Herbivores {
		ai.UpdateBoids(stk.NFrames, e.Herbivores[i], &e.Astro.body, e.wo)
		e.Herbivores[i].Move(e.wo)
	}

	return nil
}

func randPoint(xmax, ymax float64) geom.Point {
	return geom.Pt(rand.Float64()*xmax, rand.Float64()*ymax)
}
<commit_msg>Don't use nils to keep track of treasure on the world.<commit_after>// © 2012 the Minima Authors under the MIT license. See AUTHORS for the list of authors.

package main

import (
	"bufio"
	"code.google.com/p/min-game/ai"
	"code.google.com/p/min-game/animal"
	"code.google.com/p/min-game/geom"
	"code.google.com/p/min-game/item"
	"code.google.com/p/min-game/ui"
	"code.google.com/p/min-game/world"
	"encoding/json"
	"io"
	"math/rand"
)

var TileSize = world.TileSize

type Game struct {
	wo         *world.World
	cam        ui.Camera
	base       Base
	Astro      *Player
	Herbivores []animal.Herbivores
	Treasure   []item.Treasure
}

// ReadGame returns a *Game, read from the given
// reader.
func ReadGame(r io.Reader) (*Game, error) {
	g := new(Game)
	in := bufio.NewReader(r)
	var err error
	if g.wo, err = world.Read(in); err != nil {
		return nil, err
	}
	g.cam = ui.Camera{Torus: g.wo.Pixels, Dims: ScreenDims}
	crashSite := geom.Pt(float64(g.wo.X0), float64(g.wo.Y0)).Mul(TileSize)
	g.Astro = NewPlayer(g.wo, crashSite)
	g.base = NewBase(crashSite)

	if err := json.NewDecoder(in).Decode(&g); err != nil {
		panic(err)
	}
	g.CenterOnTile(g.wo.Tile(g.Astro.body.Center()))
	return g, nil
}

func (e *Game) Transparent() bool {
	return false
}

// CenterOnTile centers the display on a given tile.
func (e *Game) CenterOnTile(x, y int) {
	pt := geom.Pt(float64(x), float64(y))
	halfTile := TileSize.Div(geom.Pt(2, 2))
	e.cam.Center(pt.Mul(TileSize).Add(halfTile))
}

func (e *Game) Draw(d ui.Drawer) {
	pt := ScreenDims.Div(TileSize)
	w, h := int(pt.X), int(pt.Y)
	x0, y0 := e.wo.Tile(e.cam.Pt)

	for x := x0; x <= x0+w; x++ {
		for y := y0; y <= y0+h; y++ {
			l := e.wo.At(x, y)
			e.cam.Draw(d, ui.Sprite{
				Name:   l.Terrain.Name,
				Bounds: geom.Rectangle{geom.Pt(0, 0), TileSize},
				Shade:  shade(l),
			}, geom.Pt(float64(x), float64(y)).Mul(TileSize))
		}
	}

	e.base.Draw(d, e.cam)
	for _, t := range e.Treasure {
		if t.Item == nil {
			continue
		}
		e.cam.Draw(d, ui.Sprite{
			Name:   "Present",
			Bounds: geom.Rect(0, 0, t.Box.Dx(), t.Box.Dy()),
			Shade:  shade(e.wo.At(e.wo.Tile(t.Box.Center()))),
		}, t.Box.Min)
	}
	e.Astro.Draw(d, e.cam)
	for i := range e.Herbivores {
		e.Herbivores[i].Draw(d, e.cam)
	}

	e.Astro.drawO2(d)

	if !*debug {
		return
	}
	d.SetFont(DialogFont, 8)
	d.SetColor(White)
	sz := d.TextSize(e.Astro.info)
	d.Draw(e.Astro.info, geom.Pt(0, ScreenDims.Y-sz.Y))
}

// Shade returns the shade value for a location.
func shade(l *world.Loc) float32 {
	const minSh = 0.15
	const slope = (1 - minSh) / world.MaxElevation
	return slope*float32(l.Elevation-l.Depth) + minSh
}

func (ex *Game) Handle(stk *ui.ScreenStack, ev ui.Event) error {
	k, ok := ev.(ui.Key)
	if !ok || !k.Down {
		return nil
	}

	switch k.Button {
	case ui.Menu:
		stk.Push(NewPauseScreen(ex.Astro))
	case ui.Action:
		for i := 0; i < len(ex.Treasure); i++ {
			t := &ex.Treasure[i]
			if !ex.wo.Pixels.Overlaps(ex.Astro.body.Box, t.Box) {
				continue
			}
			scr := NewNormalMessage("You don't have room for that in your pack.")
			if ex.Astro.PutPack(t.Item) {
				scr = NewNormalMessage("Bravo! You got the " + t.Item.Name + "!")
				ex.Treasure[i] = ex.Treasure[len(ex.Treasure)-1]
				ex.Treasure = ex.Treasure[:len(ex.Treasure)-1]
			}
			stk.Push(scr)
			return nil
		}
		if ex.wo.Pixels.Overlaps(ex.Astro.body.Box, ex.base.Box) {
			stk.Push(NewBaseScreen(ex.Astro, &ex.base))
			return nil
		}
	case ui.Drop:
		if ex.Astro.Held == nil {
			break
		}
		dropped := ex.Astro.Held
		ex.Astro.Held = nil
		//TODO: we do this a lot…
		i := 0
		for ; i < len(ex.Treasure); i++ {
			if ex.Treasure[i].Item == nil {
				ex.Treasure[i].Item = dropped
				ex.Treasure[i].Box = ex.Astro.body.Box
				break
			}
		}
		if i == len(ex.Treasure) {
			ex.Treasure = append(ex.Treasure, item.Treasure{dropped, ex.Astro.body.Box})
		}
	}
	return nil
}

func (e *Game) Update(stk *ui.ScreenStack) error {
	const speed = 4 // px

	if e.Astro.o2 == 0 && !*debug {
		if et := e.Astro.FindEtele(); et == nil {
			stk.Push(NewGameOverScreen())
		} else {
			et.Uses--
			e.Astro.body.Vel = geom.Pt(0, 0)
			dims := geom.Pt(e.Astro.body.Box.Dx(), e.Astro.body.Box.Dy())
			e.Astro.body.Box.Min = e.base.Box.Min
			e.Astro.body.Box.Max = e.base.Box.Min.Add(dims)
			e.Astro.RefillO2()
		}
	}

	e.Astro.body.Vel = geom.Pt(0, 0)
	if stk.Buttons&ui.Left != 0 {
		e.Astro.body.Vel.X -= speed
	}
	if stk.Buttons&ui.Right != 0 {
		e.Astro.body.Vel.X += speed
	}
	if stk.Buttons&ui.Down != 0 {
		e.Astro.body.Vel.Y += speed
	}
	if stk.Buttons&ui.Up != 0 {
		e.Astro.body.Vel.Y -= speed
	}
	e.Astro.Move(e.wo)
	e.cam.Center(e.Astro.body.Box.Center())

	for i := range e.Herbivores {
		ai.UpdateBoids(stk.NFrames, e.Herbivores[i], &e.Astro.body, e.wo)
		e.Herbivores[i].Move(e.wo)
	}

	return nil
}

func randPoint(xmax, ymax float64) geom.Point {
	return geom.Pt(rand.Float64()*xmax, rand.Float64()*ymax)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/google/gopacket"
	"github.com/google/gopacket/pcap"
)

func handlePacket(packet gopacket.Packet) {
	fmt.Println("I got a packet!")
}

func main() {

	if handle, err := pcap.OpenLive("en0", 1600, true, pcap.BlockForever); err != nil {
		panic(err)
	} else {
		packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
		for packet := range packetSource.Packets() {
			handlePacket(packet)
		}
	}
}
<commit_msg>Print if its tcp<commit_after>package main

import (
	"fmt"

	"github.com/google/gopacket"
	"github.com/google/gopacket/pcap"
)

func handlePacket(packet gopacket.Packet) {
	if transport := packet.TransportLayer(); transport != nil {
		fmt.Println(transport.LayerType())
	}
}

func main() {

	if handle, err := pcap.OpenLive("en0", 1600, true, pcap.BlockForever); err != nil {
		panic(err)
	} else {
		packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
		for packet := range packetSource.Packets() {
			handlePacket(packet)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package oauth2 provides support for making
// OAuth2 authorized and authenticated HTTP requests.
// It can additionally grant authorization with Bearer JWT.
package oauth2 // import "golang.org/x/oauth2"

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"net/url"
	"strings"
	"sync"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/internal"
)

// NoContext is the default context you should supply if not using
// your own context.Context (see https://golang.org/x/net/context).
var NoContext = context.TODO()

// Config describes a typical 3-legged OAuth2 flow, with both the
// client application information and the server's endpoint URLs.
type Config struct {
	// ClientID is the application's ID.
	ClientID string

	// ClientSecret is the application's secret.
	ClientSecret string

	// Endpoint contains the resource server's token endpoint
	// URLs. These are constants specific to each server and are
	// often available via site-specific packages, such as
	// google.Endpoint or github.Endpoint.
	Endpoint Endpoint

	// RedirectURL is the URL to redirect users going through
	// the OAuth flow, after the resource owner's URLs.
	RedirectURL string

	// Scope specifies optional requested permissions.
	Scopes []string
}

// A TokenSource is anything that can return a token.
type TokenSource interface {
	// Token returns a token or an error.
	// Token must be safe for concurrent use by multiple goroutines.
	// The returned Token must not be modified.
	Token() (*Token, error)
}

// Endpoint contains the OAuth 2.0 provider's authorization and token
// endpoint URLs.
type Endpoint struct {
	AuthURL  string
	TokenURL string
}

var (
	// AccessTypeOnline and AccessTypeOffline are options passed
	// to the Options.AuthCodeURL method. They modify the
	// "access_type" field that gets sent in the URL returned by
	// AuthCodeURL.
	//
	// Online is the default if neither is specified. If your
	// application needs to refresh access tokens when the user
	// is not present at the browser, then use offline. This will
	// result in your application obtaining a refresh token the
	// first time your application exchanges an authorization
	// code for a user.
	AccessTypeOnline  AuthCodeOption = SetAuthURLParam("access_type", "online")
	AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline")

	// ApprovalForce forces the users to view the consent dialog
	// and confirm the permissions request at the URL returned
	// from AuthCodeURL, even if they've already done so.
	ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force")
)

// An AuthCodeOption is passed to Config.AuthCodeURL.
type AuthCodeOption interface {
	setValue(url.Values)
}

type setParam struct{ k, v string }

func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }

// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters
// to a provider's authorization endpoint.
func SetAuthURLParam(key, value string) AuthCodeOption {
	return setParam{key, value}
}

// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
// that asks for permissions for the required scopes explicitly.
//
// State is a token to protect the user from CSRF attacks. You must
// always provide a non-zero string and validate that it matches the
// the state query parameter on your redirect callback.
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
//
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
// as ApprovalForce.
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
	var buf bytes.Buffer
	buf.WriteString(c.Endpoint.AuthURL)
	v := url.Values{
		"response_type": {"code"},
		"client_id":     {c.ClientID},
		"redirect_uri":  internal.CondVal(c.RedirectURL),
		"scope":         internal.CondVal(strings.Join(c.Scopes, " ")),
		"state":         internal.CondVal(state),
	}
	for _, opt := range opts {
		opt.setValue(v)
	}
	if strings.Contains(c.Endpoint.AuthURL, "?") {
		buf.WriteByte('&')
	} else {
		buf.WriteByte('?')
	}
	buf.WriteString(v.Encode())
	return buf.String()
}

// PasswordCredentialsToken converts a resource owner username and password
// pair into a token.
//
// Per the RFC, this grant type should only be used "when there is a high
// degree of trust between the resource owner and the client (e.g., the client
// is part of the device operating system or a highly privileged application),
// and when other authorization grant types are not available."
// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
//
// The HTTP client to use is derived from the context.
// If nil, http.DefaultClient is used.
func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
	return retrieveToken(ctx, c, url.Values{
		"grant_type": {"password"},
		"username":   {username},
		"password":   {password},
		"scope":      internal.CondVal(strings.Join(c.Scopes, " ")),
	})
}

// GetTokenBasicAuth - use Basic Auth (username/password) to get a token
//
// Pass in username/password and a reader to provide the POST body
//
// Returns the token, or err
//
// Use it like something like this:
//
//		if pb, err := json.Marshal(postBody); err == nil {
// 			fmt.Println(string(pb))
// 			postBodyReader := bytes.NewReader(pb)
// 			token, err := oa2.GetTokenBasicAuth(oauth2.NoContext, "user1", "passwd1", postBodyReader)
// 			fmt.Printf("token:%v:err:%v:", token, err)
// 		} else {
// 			fmt.Printf("json.Marshal:err:%v:", err)
// 		}
//
// This works for APIs such as these github calls:
//
// https://developer.github.com/enterprise/2.1/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app
//
//		oa2 := oauth2.Config{
// 		    ClientID:     "clientid1",
// 		    ClientSecret: "clientSecret1",
// 	    	Scopes:       []string{"repo", "public_repo", "gist", "repo:status", "user"},
// 	    	Endpoint: oauth2.Endpoint{
// 	        	AuthURL:  "https://mygithub.com/api/v3/authorizations",
// 	        	TokenURL: "https://mygithub.com/api/v3/authorizations",
//	 	    },
// 		}

// 		postBody := struct {
// 			ClientId   string `json:"client_id"`
// 			ClientSecret   string `json:"client_secret"`
// 			Note   string `json:"note"`
// 			Scopes      []string `json:"scopes"`
// 		}{
// 			oa2.ClientID,
// 			oa2.ClientSecret,
// 			"my oauth",
// 			[]string{"public_repo"},
// 		}
//
// 		pb, err := json.Marshal(postBody)
//		...
// 		token, err := oa2.GetTokenBasicAuth(oauth2.NoContext, "bmcquee", "3e4r#E$R", bytes.NewReader(pb))
//
func (c *Config) GetTokenBasicAuth(ctx context.Context, username, password string, postBodyReader io.Reader) (*Token, error) {

	return retrieveTokenBasicAuth(ctx, username, password, c.Endpoint.TokenURL, postBodyReader)

}

// Exchange converts an authorization code into a token.
//
// It is used after a resource provider redirects the user back
// to the Redirect URI (the URL obtained from AuthCodeURL).
//
// The HTTP client to use is derived from the context.
// If a client is not provided via the context, http.DefaultClient is used.
//
// The code will be in the *http.Request.FormValue("code"). Before
// calling Exchange, be sure to validate FormValue("state").
func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
	return retrieveToken(ctx, c, url.Values{
		"grant_type":   {"authorization_code"},
		"code":         {code},
		"redirect_uri": internal.CondVal(c.RedirectURL),
		"scope":        internal.CondVal(strings.Join(c.Scopes, " ")),
	})
}

// Client returns an HTTP client using the provided token.
// The token will auto-refresh as necessary. The underlying
// HTTP transport will be obtained using the provided context.
// The returned client and its Transport should not be modified.
func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
	return NewClient(ctx, c.TokenSource(ctx, t))
}

// TokenSource returns a TokenSource that returns t until t expires,
// automatically refreshing it as necessary using the provided context.
//
// Most users will use Config.Client instead.
func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
	tkr := &tokenRefresher{
		ctx:  ctx,
		conf: c,
	}
	if t != nil {
		tkr.refreshToken = t.RefreshToken
	}
	return &reuseTokenSource{
		t:   t,
		new: tkr,
	}
}

// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
// HTTP requests to renew a token using a RefreshToken.
type tokenRefresher struct {
	ctx          context.Context // used to get HTTP requests
	conf         *Config
	refreshToken string
}

// WARNING: Token is not safe for concurrent access, as it
// updates the tokenRefresher's refreshToken field.
// Within this package, it is used by reuseTokenSource which
// synchronizes calls to this method with its own mutex.
func (tf *tokenRefresher) Token() (*Token, error) {
	if tf.refreshToken == "" {
		return nil, errors.New("oauth2: token expired and refresh token is not set")
	}

	tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{
		"grant_type":    {"refresh_token"},
		"refresh_token": {tf.refreshToken},
	})

	if err != nil {
		return nil, err
	}
	if tf.refreshToken != tk.RefreshToken {
		tf.refreshToken = tk.RefreshToken
	}
	return tk, err
}

// reuseTokenSource is a TokenSource that holds a single token in memory
// and validates its expiry before each call to retrieve it with
// Token. If it's expired, it will be auto-refreshed using the
// new TokenSource.
type reuseTokenSource struct {
	new TokenSource // called when t is expired.

	mu sync.Mutex // guards t
	t  *Token
}

// Token returns the current token if it's still valid, else will
// refresh the current token (using r.Context for HTTP client
// information) and return the new one.
func (s *reuseTokenSource) Token() (*Token, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.t.Valid() {
		return s.t, nil
	}
	t, err := s.new.Token()
	if err != nil {
		return nil, err
	}
	s.t = t
	return t, nil
}

// StaticTokenSource returns a TokenSource that always returns the same token.
// Because the provided token t is never refreshed, StaticTokenSource is only
// useful for tokens that never expire.
func StaticTokenSource(t *Token) TokenSource {
	return staticTokenSource{t}
}

// staticTokenSource is a TokenSource that always returns the same Token.
type staticTokenSource struct {
	t *Token
}

func (s staticTokenSource) Token() (*Token, error) {
	return s.t, nil
}

// HTTPClient is the context key to use with golang.org/x/net/context's
// WithValue function to associate an *http.Client value with a context.
var HTTPClient internal.ContextKey

// NewClient creates an *http.Client from a Context and TokenSource.
// The returned client is not valid beyond the lifetime of the context.
//
// As a special case, if src is nil, a non-OAuth2 client is returned
// using the provided context. This exists to support related OAuth2
// packages.
func NewClient(ctx context.Context, src TokenSource) *http.Client {
	if src == nil {
		c, err := internal.ContextClient(ctx)
		if err != nil {
			return &http.Client{Transport: internal.ErrorTransport{err}}
		}
		return c
	}
	return &http.Client{
		Transport: &Transport{
			Base:   internal.ContextTransport(ctx),
			Source: ReuseTokenSource(nil, src),
		},
	}
}

// ReuseTokenSource returns a TokenSource which repeatedly returns the
// same token as long as it's valid, starting with t.
// When its cached token is invalid, a new token is obtained from src.
//
// ReuseTokenSource is typically used to reuse tokens from a cache
// (such as a file on disk) between runs of a program, rather than
// obtaining new tokens unnecessarily.
//
// The initial token t may be nil, in which case the TokenSource is
// wrapped in a caching version if it isn't one already. This also
// means it's always safe to wrap ReuseTokenSource around any other
// TokenSource without adverse effects.
func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
	// Don't wrap a reuseTokenSource in itself. That would work,
	// but cause an unnecessary number of mutex operations.
	// Just build the equivalent one.
	if rt, ok := src.(*reuseTokenSource); ok {
		if t == nil {
			// Just use it directly.
			return rt
		}
		src = rt.new
	}
	return &reuseTokenSource{
		t:   t,
		new: src,
	}
}
<commit_msg>amend annoying comment<commit_after>// Copyright 2014 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package oauth2 provides support for making
// OAuth2 authorized and authenticated HTTP requests.
// It can additionally grant authorization with Bearer JWT.
package oauth2 // import "golang.org/x/oauth2"

import (
	"bytes"
	"errors"
	"io"
	"net/http"
	"net/url"
	"strings"
	"sync"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/internal"
)

// NoContext is the default context you should supply if not using
// your own context.Context (see https://golang.org/x/net/context).
var NoContext = context.TODO()

// Config describes a typical 3-legged OAuth2 flow, with both the
// client application information and the server's endpoint URLs.
type Config struct {
	// ClientID is the application's ID.
	ClientID string

	// ClientSecret is the application's secret.
	ClientSecret string

	// Endpoint contains the resource server's token endpoint
	// URLs. These are constants specific to each server and are
	// often available via site-specific packages, such as
	// google.Endpoint or github.Endpoint.
	Endpoint Endpoint

	// RedirectURL is the URL to redirect users going through
	// the OAuth flow, after the resource owner's URLs.
	RedirectURL string

	// Scope specifies optional requested permissions.
	Scopes []string
}

// A TokenSource is anything that can return a token.
type TokenSource interface {
	// Token returns a token or an error.
	// Token must be safe for concurrent use by multiple goroutines.
	// The returned Token must not be modified.
	Token() (*Token, error)
}

// Endpoint contains the OAuth 2.0 provider's authorization and token
// endpoint URLs.
type Endpoint struct {
	AuthURL  string
	TokenURL string
}

var (
	// AccessTypeOnline and AccessTypeOffline are options passed
	// to the Options.AuthCodeURL method. They modify the
	// "access_type" field that gets sent in the URL returned by
	// AuthCodeURL.
	//
	// Online is the default if neither is specified. If your
	// application needs to refresh access tokens when the user
	// is not present at the browser, then use offline. This will
	// result in your application obtaining a refresh token the
	// first time your application exchanges an authorization
	// code for a user.
	AccessTypeOnline  AuthCodeOption = SetAuthURLParam("access_type", "online")
	AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline")

	// ApprovalForce forces the users to view the consent dialog
	// and confirm the permissions request at the URL returned
	// from AuthCodeURL, even if they've already done so.
	ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force")
)

// An AuthCodeOption is passed to Config.AuthCodeURL.
type AuthCodeOption interface {
	setValue(url.Values)
}

type setParam struct{ k, v string }

func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }

// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters
// to a provider's authorization endpoint.
func SetAuthURLParam(key, value string) AuthCodeOption {
	return setParam{key, value}
}

// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
// that asks for permissions for the required scopes explicitly.
//
// State is a token to protect the user from CSRF attacks. You must
// always provide a non-zero string and validate that it matches the
// the state query parameter on your redirect callback.
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
//
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
// as ApprovalForce.
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
	var buf bytes.Buffer
	buf.WriteString(c.Endpoint.AuthURL)
	v := url.Values{
		"response_type": {"code"},
		"client_id":     {c.ClientID},
		"redirect_uri":  internal.CondVal(c.RedirectURL),
		"scope":         internal.CondVal(strings.Join(c.Scopes, " ")),
		"state":         internal.CondVal(state),
	}
	for _, opt := range opts {
		opt.setValue(v)
	}
	if strings.Contains(c.Endpoint.AuthURL, "?") {
		buf.WriteByte('&')
	} else {
		buf.WriteByte('?')
	}
	buf.WriteString(v.Encode())
	return buf.String()
}

// PasswordCredentialsToken converts a resource owner username and password
// pair into a token.
//
// Per the RFC, this grant type should only be used "when there is a high
// degree of trust between the resource owner and the client (e.g., the client
// is part of the device operating system or a highly privileged application),
// and when other authorization grant types are not available."
// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
//
// The HTTP client to use is derived from the context.
// If nil, http.DefaultClient is used.
func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
	return retrieveToken(ctx, c, url.Values{
		"grant_type": {"password"},
		"username":   {username},
		"password":   {password},
		"scope":      internal.CondVal(strings.Join(c.Scopes, " ")),
	})
}

// GetTokenBasicAuth - use Basic Auth (username/password) to get a token
//
// Pass in username/password and a reader to provide the POST body
//
// Returns the token, or err
//
// Use it like something like this:
//
//		if pb, err := json.Marshal(postBody); err == nil {
// 			fmt.Println(string(pb))
// 			postBodyReader := bytes.NewReader(pb)
// 			token, err := oa2.GetTokenBasicAuth(oauth2.NoContext, "user1", "passwd1", postBodyReader)
// 			fmt.Printf("token:%v:err:%v:", token, err)
// 		} else {
// 			fmt.Printf("json.Marshal:err:%v:", err)
// 		}
//
// This works for APIs such as these github calls:
//
// https://developer.github.com/enterprise/2.1/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app
//
//		oa2 := oauth2.Config{
// 		    ClientID:     "clientid1",
// 		    ClientSecret: "clientSecret1",
// 	    	Scopes:       []string{"repo", "public_repo", "gist", "repo:status", "user"},
// 	    	Endpoint: oauth2.Endpoint{
// 	        	AuthURL:  "https://mygithub.com/api/v3/authorizations",
// 	        	TokenURL: "https://mygithub.com/api/v3/authorizations",
//	 	    },
// 		}

// 		postBody := struct {
// 			ClientId   string `json:"client_id"`
// 			ClientSecret   string `json:"client_secret"`
// 			Note   string `json:"note"`
// 			Scopes      []string `json:"scopes"`
// 		}{
// 			oa2.ClientID,
// 			oa2.ClientSecret,
// 			"my oauth",
// 			[]string{"public_repo"},
// 		}
//
// 		pb, err := json.Marshal(postBody)
//		...
// 		token, err := oa2.GetTokenBasicAuth(oauth2.NoContext, "zup", "foo", bytes.NewReader(pb))
//
func (c *Config) GetTokenBasicAuth(ctx context.Context, username, password string, postBodyReader io.Reader) (*Token, error) {

	return retrieveTokenBasicAuth(ctx, username, password, c.Endpoint.TokenURL, postBodyReader)

}

// Exchange converts an authorization code into a token.
//
// It is used after a resource provider redirects the user back
// to the Redirect URI (the URL obtained from AuthCodeURL).
//
// The HTTP client to use is derived from the context.
// If a client is not provided via the context, http.DefaultClient is used.
//
// The code will be in the *http.Request.FormValue("code"). Before
// calling Exchange, be sure to validate FormValue("state").
func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
	return retrieveToken(ctx, c, url.Values{
		"grant_type":   {"authorization_code"},
		"code":         {code},
		"redirect_uri": internal.CondVal(c.RedirectURL),
		"scope":        internal.CondVal(strings.Join(c.Scopes, " ")),
	})
}

// Client returns an HTTP client using the provided token.
// The token will auto-refresh as necessary. The underlying
// HTTP transport will be obtained using the provided context.
// The returned client and its Transport should not be modified.
func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
	return NewClient(ctx, c.TokenSource(ctx, t))
}

// TokenSource returns a TokenSource that returns t until t expires,
// automatically refreshing it as necessary using the provided context.
//
// Most users will use Config.Client instead.
func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
	tkr := &tokenRefresher{
		ctx:  ctx,
		conf: c,
	}
	if t != nil {
		tkr.refreshToken = t.RefreshToken
	}
	return &reuseTokenSource{
		t:   t,
		new: tkr,
	}
}

// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
// HTTP requests to renew a token using a RefreshToken.
type tokenRefresher struct {
	ctx          context.Context // used to get HTTP requests
	conf         *Config
	refreshToken string
}

// WARNING: Token is not safe for concurrent access, as it
// updates the tokenRefresher's refreshToken field.
// Within this package, it is used by reuseTokenSource which
// synchronizes calls to this method with its own mutex.
func (tf *tokenRefresher) Token() (*Token, error) {
	if tf.refreshToken == "" {
		return nil, errors.New("oauth2: token expired and refresh token is not set")
	}

	tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{
		"grant_type":    {"refresh_token"},
		"refresh_token": {tf.refreshToken},
	})

	if err != nil {
		return nil, err
	}
	if tf.refreshToken != tk.RefreshToken {
		tf.refreshToken = tk.RefreshToken
	}
	return tk, err
}

// reuseTokenSource is a TokenSource that holds a single token in memory
// and validates its expiry before each call to retrieve it with
// Token. If it's expired, it will be auto-refreshed using the
// new TokenSource.
type reuseTokenSource struct {
	new TokenSource // called when t is expired.

	mu sync.Mutex // guards t
	t  *Token
}

// Token returns the current token if it's still valid, else will
// refresh the current token (using r.Context for HTTP client
// information) and return the new one.
func (s *reuseTokenSource) Token() (*Token, error) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.t.Valid() {
		return s.t, nil
	}
	t, err := s.new.Token()
	if err != nil {
		return nil, err
	}
	s.t = t
	return t, nil
}

// StaticTokenSource returns a TokenSource that always returns the same token.
// Because the provided token t is never refreshed, StaticTokenSource is only
// useful for tokens that never expire.
func StaticTokenSource(t *Token) TokenSource {
	return staticTokenSource{t}
}

// staticTokenSource is a TokenSource that always returns the same Token.
type staticTokenSource struct {
	t *Token
}

func (s staticTokenSource) Token() (*Token, error) {
	return s.t, nil
}

// HTTPClient is the context key to use with golang.org/x/net/context's
// WithValue function to associate an *http.Client value with a context.
var HTTPClient internal.ContextKey

// NewClient creates an *http.Client from a Context and TokenSource.
// The returned client is not valid beyond the lifetime of the context.
//
// As a special case, if src is nil, a non-OAuth2 client is returned
// using the provided context. This exists to support related OAuth2
// packages.
func NewClient(ctx context.Context, src TokenSource) *http.Client {
	if src == nil {
		c, err := internal.ContextClient(ctx)
		if err != nil {
			return &http.Client{Transport: internal.ErrorTransport{err}}
		}
		return c
	}
	return &http.Client{
		Transport: &Transport{
			Base:   internal.ContextTransport(ctx),
			Source: ReuseTokenSource(nil, src),
		},
	}
}

// ReuseTokenSource returns a TokenSource which repeatedly returns the
// same token as long as it's valid, starting with t.
// When its cached token is invalid, a new token is obtained from src.
//
// ReuseTokenSource is typically used to reuse tokens from a cache
// (such as a file on disk) between runs of a program, rather than
// obtaining new tokens unnecessarily.
//
// The initial token t may be nil, in which case the TokenSource is
// wrapped in a caching version if it isn't one already. This also
// means it's always safe to wrap ReuseTokenSource around any other
// TokenSource without adverse effects.
func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
	// Don't wrap a reuseTokenSource in itself. That would work,
	// but cause an unnecessary number of mutex operations.
	// Just build the equivalent one.
	if rt, ok := src.(*reuseTokenSource); ok {
		if t == nil {
			// Just use it directly.
			return rt
		}
		src = rt.new
	}
	return &reuseTokenSource{
		t:   t,
		new: src,
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package oauth2 provides support for making
// OAuth2 authorized and authenticated HTTP requests.
// It can additionally grant authorization with Bearer JWT.
package oauth2

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

type tokenRespBody struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	RefreshToken string `json:"refresh_token"`
	ExpiresIn    int64  `json:"expires_in"` // in seconds
	IdToken      string `json:"id_token"`
}

// TokenFetcher refreshes or fetches a new access token from the
// provider. It should return an error if it's not capable of
// retrieving a token.
type TokenFetcher interface {
	// FetchToken retrieves a new access token for the provider.
	// If the implementation doesn't know how to retrieve a new token,
	// it returns an error. The existing token may be nil.
	FetchToken(existing *Token) (*Token, error)
}

// Options represents options to provide OAuth 2.0 client credentials
// and access level. A sample configuration:
type Options struct {
	// ClientID is the OAuth client identifier used when communicating with
	// the configured OAuth provider.
	ClientID string `json:"client_id"`

	// ClientSecret is the OAuth client secret used when communicating with
	// the configured OAuth provider.
	ClientSecret string `json:"client_secret"`

	// RedirectURL is the URL to which the user will be returned after
	// granting (or denying) access.
	RedirectURL string `json:"redirect_url"`

	// Scopes optionally specifies a list of requested permission scopes.
	Scopes []string `json:"scopes,omitempty"`
}

// NewConfig creates a generic OAuth 2.0 configuration that talks
// to an OAuth 2.0 provider specified with authURL and tokenURL.
func NewConfig(opts *Options, authURL, tokenURL string) (*Config, error) {
	aURL, err := url.Parse(authURL)
	if err != nil {
		return nil, err
	}
	tURL, err := url.Parse(tokenURL)
	if err != nil {
		return nil, err
	}
	if opts.ClientID == "" {
		return nil, errors.New("oauth2: missing client ID")
	}
	return &Config{
		opts:     opts,
		authURL:  aURL,
		tokenURL: tURL,
	}, nil
}

// Config represents the configuration of an OAuth 2.0 consumer client.
type Config struct {
	// Client is the HTTP client to be used to retrieve
	// tokens from the OAuth 2.0 provider.
	Client *http.Client

	// Transport is the http.RoundTripper to be used
	// to construct new oauth2.Transport instances from
	// this configuration.
	Transport http.RoundTripper

	opts *Options
	// AuthURL is the URL the user will be directed to
	// in order to grant access.
	authURL *url.URL
	// TokenURL is the URL used to retrieve OAuth tokens.
	tokenURL *url.URL
}

// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
// that asks for permissions for the required scopes explicitly.
//
// State is a token to protect the user from CSRF attacks. You must
// always provide a non-zero string and validate that it matches the
// the state query parameter on your redirect callback.
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
//
// Access type is an OAuth extension that gets sent as the
// "access_type" field in the URL from AuthCodeURL.
// It may be "online" (default) or "offline".
// If your application needs to refresh access tokens when the
// user is not present at the browser, then use offline. This
// will result in your application obtaining a refresh token
// the first time your application exchanges an authorization
// code for a user.
//
// Approval prompt indicates whether the user should be
// re-prompted for consent. If set to "auto" (default) the
// user will be prompted only if they haven't previously
// granted consent and the code can only be exchanged for an
// access token. If set to "force" the user will always be prompted,
// and the code can be exchanged for a refresh token.
func (c *Config) AuthCodeURL(state, accessType, prompt string) (authURL string) {
	u := *c.authURL
	v := url.Values{
		"response_type":   {"code"},
		"client_id":       {c.opts.ClientID},
		"redirect_uri":    condVal(c.opts.RedirectURL),
		"scope":           condVal(strings.Join(c.opts.Scopes, " ")),
		"state":           condVal(state),
		"access_type":     condVal(accessType),
		"approval_prompt": condVal(prompt),
	}
	q := v.Encode()
	if u.RawQuery == "" {
		u.RawQuery = q
	} else {
		u.RawQuery += "&" + q
	}
	return u.String()
}

// NewTransport creates a new authorizable transport. It doesn't
// initialize the new transport with a token, so after creation,
// you need to set a valid token (or an expired token with a valid
// refresh token) in order to be able to do authorized requests.
func (c *Config) NewTransport() *Transport {
	return NewTransport(c.transport(), c, nil)
}

// NewTransportWithCode exchanges the OAuth 2.0 authorization code with
// the provider to fetch a new access token (and refresh token). Once
// it succesffully retrieves a new token, creates a new transport
// authorized with it.
func (c *Config) NewTransportWithCode(code string) (*Transport, error) {
	token, err := c.Exchange(code)
	if err != nil {
		return nil, err
	}
	return NewTransport(c.transport(), c, token), nil
}

// FetchToken retrieves a new access token and updates the existing token
// with the newly fetched credentials. If existing token doesn't
// contain a refresh token, it returns an error.
func (c *Config) FetchToken(existing *Token) (*Token, error) {
	if existing == nil || existing.RefreshToken == "" {
		return nil, errors.New("oauth2: cannot fetch access token without refresh token")
	}
	return c.retrieveToken(url.Values{
		"grant_type":    {"refresh_token"},
		"refresh_token": {existing.RefreshToken},
	})
}

// Exchange exchanges the authorization code with the OAuth 2.0 provider
// to retrieve a new access token.
func (c *Config) Exchange(code string) (*Token, error) {
	return c.retrieveToken(url.Values{
		"grant_type":   {"authorization_code"},
		"code":         {code},
		"redirect_uri": condVal(c.opts.RedirectURL),
		"scope":        condVal(strings.Join(c.opts.Scopes, " ")),
	})
}

func (c *Config) retrieveToken(v url.Values) (*Token, error) {
	v.Set("client_id", c.opts.ClientID)
	bustedAuth := !providerAuthHeaderWorks(c.tokenURL.String())
	if bustedAuth && c.opts.ClientSecret != "" {
		v.Set("client_secret", c.opts.ClientSecret)
	}
	req, err := http.NewRequest("POST", c.tokenURL.String(), strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth && c.opts.ClientSecret != "" {
		req.SetBasicAuth(c.opts.ClientID, c.opts.ClientSecret)
	}
	r, err := c.client().Do(req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if c := r.StatusCode; c < 200 || c > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
	}

	resp := &tokenRespBody{}
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return nil, err
		}
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		resp.AccessToken = vals.Get("access_token")
		resp.TokenType = vals.Get("token_type")
		resp.RefreshToken = vals.Get("refresh_token")
		resp.ExpiresIn, _ = strconv.ParseInt(vals.Get("expires_in"), 10, 64)
		resp.IdToken = vals.Get("id_token")
	default:
		if err = json.NewDecoder(r.Body).Decode(&resp); err != nil {
			return nil, err
		}
	}
	token := &Token{
		AccessToken:  resp.AccessToken,
		TokenType:    resp.TokenType,
		RefreshToken: resp.RefreshToken,
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if resp.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	if resp.ExpiresIn == 0 {
		token.Expiry = time.Time{}
	} else {
		token.Expiry = time.Now().Add(time.Duration(resp.ExpiresIn) * time.Second)
	}
	if resp.IdToken != "" {
		if token.Extra == nil {
			token.Extra = make(map[string]string)
		}
		token.Extra["id_token"] = resp.IdToken
	}
	return token, nil
}

func (c *Config) transport() http.RoundTripper {
	if c.Transport != nil {
		return c.Transport
	}
	return http.DefaultTransport
}

func (c *Config) client() *http.Client {
	if c.Client != nil {
		return c.Client
	}
	return http.DefaultClient
}

func condVal(v string) []string {
	if v == "" {
		return nil
	}
	return []string{v}
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
func providerAuthHeaderWorks(tokenURL string) bool {
	if strings.HasPrefix(tokenURL, "https://accounts.google.com/") ||
		strings.HasPrefix(tokenURL, "https://github.com/") ||
		strings.HasPrefix(tokenURL, "https://api.instagram.com/") ||
		strings.HasPrefix(tokenURL, "https://www.douban.com/") {
		// Some sites fail to implement the OAuth2 spec fully.
		return false
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}
<commit_msg>Add Dropbox to the providers not supporting Auth headers. Dropbox does not support mixing URL param and Auth header for authentication but the retrieveToken method forces the client_id to be sent in the URL param.<commit_after>// Copyright 2014 The oauth2 Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package oauth2 provides support for making
// OAuth2 authorized and authenticated HTTP requests.
// It can additionally grant authorization with Bearer JWT.
package oauth2

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

type tokenRespBody struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`
	RefreshToken string `json:"refresh_token"`
	ExpiresIn    int64  `json:"expires_in"` // in seconds
	IdToken      string `json:"id_token"`
}

// TokenFetcher refreshes or fetches a new access token from the
// provider. It should return an error if it's not capable of
// retrieving a token.
type TokenFetcher interface {
	// FetchToken retrieves a new access token for the provider.
	// If the implementation doesn't know how to retrieve a new token,
	// it returns an error. The existing token may be nil.
	FetchToken(existing *Token) (*Token, error)
}

// Options represents options to provide OAuth 2.0 client credentials
// and access level. A sample configuration:
type Options struct {
	// ClientID is the OAuth client identifier used when communicating with
	// the configured OAuth provider.
	ClientID string `json:"client_id"`

	// ClientSecret is the OAuth client secret used when communicating with
	// the configured OAuth provider.
	ClientSecret string `json:"client_secret"`

	// RedirectURL is the URL to which the user will be returned after
	// granting (or denying) access.
	RedirectURL string `json:"redirect_url"`

	// Scopes optionally specifies a list of requested permission scopes.
	Scopes []string `json:"scopes,omitempty"`
}

// NewConfig creates a generic OAuth 2.0 configuration that talks
// to an OAuth 2.0 provider specified with authURL and tokenURL.
func NewConfig(opts *Options, authURL, tokenURL string) (*Config, error) {
	aURL, err := url.Parse(authURL)
	if err != nil {
		return nil, err
	}
	tURL, err := url.Parse(tokenURL)
	if err != nil {
		return nil, err
	}
	if opts.ClientID == "" {
		return nil, errors.New("oauth2: missing client ID")
	}
	return &Config{
		opts:     opts,
		authURL:  aURL,
		tokenURL: tURL,
	}, nil
}

// Config represents the configuration of an OAuth 2.0 consumer client.
type Config struct {
	// Client is the HTTP client to be used to retrieve
	// tokens from the OAuth 2.0 provider.
	Client *http.Client

	// Transport is the http.RoundTripper to be used
	// to construct new oauth2.Transport instances from
	// this configuration.
	Transport http.RoundTripper

	opts *Options
	// AuthURL is the URL the user will be directed to
	// in order to grant access.
	authURL *url.URL
	// TokenURL is the URL used to retrieve OAuth tokens.
	tokenURL *url.URL
}

// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
// that asks for permissions for the required scopes explicitly.
//
// State is a token to protect the user from CSRF attacks. You must
// always provide a non-zero string and validate that it matches the
// the state query parameter on your redirect callback.
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
//
// Access type is an OAuth extension that gets sent as the
// "access_type" field in the URL from AuthCodeURL.
// It may be "online" (default) or "offline".
// If your application needs to refresh access tokens when the
// user is not present at the browser, then use offline. This
// will result in your application obtaining a refresh token
// the first time your application exchanges an authorization
// code for a user.
//
// Approval prompt indicates whether the user should be
// re-prompted for consent. If set to "auto" (default) the
// user will be prompted only if they haven't previously
// granted consent and the code can only be exchanged for an
// access token. If set to "force" the user will always be prompted,
// and the code can be exchanged for a refresh token.
func (c *Config) AuthCodeURL(state, accessType, prompt string) (authURL string) {
	u := *c.authURL
	v := url.Values{
		"response_type":   {"code"},
		"client_id":       {c.opts.ClientID},
		"redirect_uri":    condVal(c.opts.RedirectURL),
		"scope":           condVal(strings.Join(c.opts.Scopes, " ")),
		"state":           condVal(state),
		"access_type":     condVal(accessType),
		"approval_prompt": condVal(prompt),
	}
	q := v.Encode()
	if u.RawQuery == "" {
		u.RawQuery = q
	} else {
		u.RawQuery += "&" + q
	}
	return u.String()
}

// NewTransport creates a new authorizable transport. It doesn't
// initialize the new transport with a token, so after creation,
// you need to set a valid token (or an expired token with a valid
// refresh token) in order to be able to do authorized requests.
func (c *Config) NewTransport() *Transport {
	return NewTransport(c.transport(), c, nil)
}

// NewTransportWithCode exchanges the OAuth 2.0 authorization code with
// the provider to fetch a new access token (and refresh token). Once
// it succesffully retrieves a new token, creates a new transport
// authorized with it.
func (c *Config) NewTransportWithCode(code string) (*Transport, error) {
	token, err := c.Exchange(code)
	if err != nil {
		return nil, err
	}
	return NewTransport(c.transport(), c, token), nil
}

// FetchToken retrieves a new access token and updates the existing token
// with the newly fetched credentials. If existing token doesn't
// contain a refresh token, it returns an error.
func (c *Config) FetchToken(existing *Token) (*Token, error) {
	if existing == nil || existing.RefreshToken == "" {
		return nil, errors.New("oauth2: cannot fetch access token without refresh token")
	}
	return c.retrieveToken(url.Values{
		"grant_type":    {"refresh_token"},
		"refresh_token": {existing.RefreshToken},
	})
}

// Exchange exchanges the authorization code with the OAuth 2.0 provider
// to retrieve a new access token.
func (c *Config) Exchange(code string) (*Token, error) {
	return c.retrieveToken(url.Values{
		"grant_type":   {"authorization_code"},
		"code":         {code},
		"redirect_uri": condVal(c.opts.RedirectURL),
		"scope":        condVal(strings.Join(c.opts.Scopes, " ")),
	})
}

func (c *Config) retrieveToken(v url.Values) (*Token, error) {
	v.Set("client_id", c.opts.ClientID)
	bustedAuth := !providerAuthHeaderWorks(c.tokenURL.String())
	if bustedAuth && c.opts.ClientSecret != "" {
		v.Set("client_secret", c.opts.ClientSecret)
	}
	req, err := http.NewRequest("POST", c.tokenURL.String(), strings.NewReader(v.Encode()))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	if !bustedAuth && c.opts.ClientSecret != "" {
		req.SetBasicAuth(c.opts.ClientID, c.opts.ClientSecret)
	}
	r, err := c.client().Do(req)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
	}
	if c := r.StatusCode; c < 200 || c > 299 {
		return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
	}

	resp := &tokenRespBody{}
	content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
	switch content {
	case "application/x-www-form-urlencoded", "text/plain":
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			return nil, err
		}
		vals, err := url.ParseQuery(string(body))
		if err != nil {
			return nil, err
		}
		resp.AccessToken = vals.Get("access_token")
		resp.TokenType = vals.Get("token_type")
		resp.RefreshToken = vals.Get("refresh_token")
		resp.ExpiresIn, _ = strconv.ParseInt(vals.Get("expires_in"), 10, 64)
		resp.IdToken = vals.Get("id_token")
	default:
		if err = json.NewDecoder(r.Body).Decode(&resp); err != nil {
			return nil, err
		}
	}
	token := &Token{
		AccessToken:  resp.AccessToken,
		TokenType:    resp.TokenType,
		RefreshToken: resp.RefreshToken,
	}
	// Don't overwrite `RefreshToken` with an empty value
	// if this was a token refreshing request.
	if resp.RefreshToken == "" {
		token.RefreshToken = v.Get("refresh_token")
	}
	if resp.ExpiresIn == 0 {
		token.Expiry = time.Time{}
	} else {
		token.Expiry = time.Now().Add(time.Duration(resp.ExpiresIn) * time.Second)
	}
	if resp.IdToken != "" {
		if token.Extra == nil {
			token.Extra = make(map[string]string)
		}
		token.Extra["id_token"] = resp.IdToken
	}
	return token, nil
}

func (c *Config) transport() http.RoundTripper {
	if c.Transport != nil {
		return c.Transport
	}
	return http.DefaultTransport
}

func (c *Config) client() *http.Client {
	if c.Client != nil {
		return c.Client
	}
	return http.DefaultClient
}

func condVal(v string) []string {
	if v == "" {
		return nil
	}
	return []string{v}
}

// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
// implements the OAuth2 spec correctly
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
// In summary:
// - Reddit only accepts client secret in the Authorization header
// - Dropbox accepts either it in URL param or Auth header, but not both.
// - Google only accepts URL param (not spec compliant?), not Auth header
func providerAuthHeaderWorks(tokenURL string) bool {
	if strings.HasPrefix(tokenURL, "https://accounts.google.com/") ||
		strings.HasPrefix(tokenURL, "https://github.com/") ||
		strings.HasPrefix(tokenURL, "https://api.instagram.com/") ||
		strings.HasPrefix(tokenURL, "https://www.douban.com/") ||
		strings.HasPrefix(tokenURL, "https://api.dropbox.com/") {
		// Some sites fail to implement the OAuth2 spec fully.
		return false
	}

	// Assume the provider implements the spec properly
	// otherwise. We can add more exceptions as they're
	// discovered. We will _not_ be adding configurable hooks
	// to this package to let users select server bugs.
	return true
}
<|endoftext|>


<commit_before>// Copyright 2014 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"context"
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

func TestRepositoriesService_ListContributorsStats(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/contributors", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `
[
  {
    "author": {
			"id": 1,
			"node_id": "nodeid-1"
    },
    "total": 135,
    "weeks": [
      {
        "w": 1367712000,
        "a": 6898,
        "d": 77,
        "c": 10
      }
    ]
  }
]
`)
	})

	ctx := context.Background()
	stats, _, err := client.Repositories.ListContributorsStats(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListContributorsStats returned error: %v", err)
	}

	want := []*ContributorStats{
		{
			Author: &Contributor{
				ID:     Int64(1),
				NodeID: String("nodeid-1"),
			},
			Total: Int(135),
			Weeks: []*WeeklyStats{
				{
					Week:      &Timestamp{time.Date(2013, time.May, 05, 00, 00, 00, 0, time.UTC).Local()},
					Additions: Int(6898),
					Deletions: Int(77),
					Commits:   Int(10),
				},
			},
		},
	}

	if !cmp.Equal(stats, want) {
		t.Errorf("RepositoriesService.ListContributorsStats returned %+v, want %+v", stats, want)
	}

	const methodName = "ListContributorsStats"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListContributorsStats(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListContributorsStats(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_ListCommitActivity(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/commit_activity", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `
[
  {
    "days": [0, 3, 26, 20, 39, 1, 0],
    "total": 89,
    "week": 1336280400
  }
]
`)
	})

	ctx := context.Background()
	activity, _, err := client.Repositories.ListCommitActivity(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListCommitActivity returned error: %v", err)
	}

	want := []*WeeklyCommitActivity{
		{
			Days:  []int{0, 3, 26, 20, 39, 1, 0},
			Total: Int(89),
			Week:  &Timestamp{time.Date(2012, time.May, 06, 05, 00, 00, 0, time.UTC).Local()},
		},
	}

	if !cmp.Equal(activity, want) {
		t.Errorf("RepositoriesService.ListCommitActivity returned %+v, want %+v", activity, want)
	}

	const methodName = "ListCommitActivity"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListCommitActivity(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListCommitActivity(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_ListCodeFrequency(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/code_frequency", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `[[1302998400, 1124, -435]]`)
	})

	ctx := context.Background()
	code, _, err := client.Repositories.ListCodeFrequency(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListCodeFrequency returned error: %v", err)
	}

	want := []*WeeklyStats{{
		Week:      &Timestamp{time.Date(2011, time.April, 17, 00, 00, 00, 0, time.UTC).Local()},
		Additions: Int(1124),
		Deletions: Int(-435),
	}}

	if !cmp.Equal(code, want) {
		t.Errorf("RepositoriesService.ListCodeFrequency returned %+v, want %+v", code, want)
	}

	const methodName = "ListCodeFrequency"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListCodeFrequency(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListCodeFrequency(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_Participation(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/participation", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `
{
  "all": [
    11,21,15,2,8,1,8,23,17,21,11,10,33,
    91,38,34,22,23,32,3,43,87,71,18,13,5,
    13,16,66,27,12,45,110,117,13,8,18,9,19,
    26,39,12,20,31,46,91,45,10,24,9,29,7
  ],
  "owner": [
    3,2,3,0,2,0,5,14,7,9,1,5,0,
    48,19,2,0,1,10,2,23,40,35,8,8,2,
    10,6,30,0,2,9,53,104,3,3,10,4,7,
    11,21,4,4,22,26,63,11,2,14,1,10,3
  ]
}
`)
	})

	ctx := context.Background()
	participation, _, err := client.Repositories.ListParticipation(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListParticipation returned error: %v", err)
	}

	want := &RepositoryParticipation{
		All: []int{
			11, 21, 15, 2, 8, 1, 8, 23, 17, 21, 11, 10, 33,
			91, 38, 34, 22, 23, 32, 3, 43, 87, 71, 18, 13, 5,
			13, 16, 66, 27, 12, 45, 110, 117, 13, 8, 18, 9, 19,
			26, 39, 12, 20, 31, 46, 91, 45, 10, 24, 9, 29, 7,
		},
		Owner: []int{
			3, 2, 3, 0, 2, 0, 5, 14, 7, 9, 1, 5, 0,
			48, 19, 2, 0, 1, 10, 2, 23, 40, 35, 8, 8, 2,
			10, 6, 30, 0, 2, 9, 53, 104, 3, 3, 10, 4, 7,
			11, 21, 4, 4, 22, 26, 63, 11, 2, 14, 1, 10, 3,
		},
	}

	if !cmp.Equal(participation, want) {
		t.Errorf("RepositoriesService.ListParticipation returned %+v, want %+v", participation, want)
	}

	const methodName = "ListParticipation"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListParticipation(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListParticipation(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_ListPunchCard(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/punch_card", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `[
		  [0, 0, 5],
		  [0, 1, 43],
		  [0, 2, 21]
		]`)
	})

	ctx := context.Background()
	card, _, err := client.Repositories.ListPunchCard(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListPunchCard returned error: %v", err)
	}

	want := []*PunchCard{
		{Day: Int(0), Hour: Int(0), Commits: Int(5)},
		{Day: Int(0), Hour: Int(1), Commits: Int(43)},
		{Day: Int(0), Hour: Int(2), Commits: Int(21)},
	}

	if !cmp.Equal(card, want) {
		t.Errorf("RepositoriesService.ListPunchCard returned %+v, want %+v", card, want)
	}

	const methodName = "ListPunchCard"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListPunchCard(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListPunchCard(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_AcceptedError(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/contributors", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		// This response indicates the fork will happen asynchronously.
		w.WriteHeader(http.StatusAccepted)
		fmt.Fprint(w, `{"id":1}`)
	})

	ctx := context.Background()
	stats, _, err := client.Repositories.ListContributorsStats(ctx, "o", "r")
	if err == nil {
		t.Errorf("RepositoriesService.AcceptedError should have returned an error")
	}

	if _, ok := err.(*AcceptedError); !ok {
		t.Errorf("RepositoriesService.AcceptedError returned an AcceptedError: %v", err)
	}

	if stats != nil {
		t.Errorf("RepositoriesService.AcceptedError expected stats to be nil: %v", stats)
	}

	const methodName = "ListContributorsStats"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListContributorsStats(ctx, "o", "r")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListContributorsStats(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}
<commit_msg>Add test cases for JSON resource marshaling<commit_after>// Copyright 2014 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"context"
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

func TestRepositoriesService_ListContributorsStats(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/contributors", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `
[
  {
    "author": {
			"id": 1,
			"node_id": "nodeid-1"
    },
    "total": 135,
    "weeks": [
      {
        "w": 1367712000,
        "a": 6898,
        "d": 77,
        "c": 10
      }
    ]
  }
]
`)
	})

	ctx := context.Background()
	stats, _, err := client.Repositories.ListContributorsStats(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListContributorsStats returned error: %v", err)
	}

	want := []*ContributorStats{
		{
			Author: &Contributor{
				ID:     Int64(1),
				NodeID: String("nodeid-1"),
			},
			Total: Int(135),
			Weeks: []*WeeklyStats{
				{
					Week:      &Timestamp{time.Date(2013, time.May, 05, 00, 00, 00, 0, time.UTC).Local()},
					Additions: Int(6898),
					Deletions: Int(77),
					Commits:   Int(10),
				},
			},
		},
	}

	if !cmp.Equal(stats, want) {
		t.Errorf("RepositoriesService.ListContributorsStats returned %+v, want %+v", stats, want)
	}

	const methodName = "ListContributorsStats"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListContributorsStats(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListContributorsStats(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_ListCommitActivity(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/commit_activity", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `
[
  {
    "days": [0, 3, 26, 20, 39, 1, 0],
    "total": 89,
    "week": 1336280400
  }
]
`)
	})

	ctx := context.Background()
	activity, _, err := client.Repositories.ListCommitActivity(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListCommitActivity returned error: %v", err)
	}

	want := []*WeeklyCommitActivity{
		{
			Days:  []int{0, 3, 26, 20, 39, 1, 0},
			Total: Int(89),
			Week:  &Timestamp{time.Date(2012, time.May, 06, 05, 00, 00, 0, time.UTC).Local()},
		},
	}

	if !cmp.Equal(activity, want) {
		t.Errorf("RepositoriesService.ListCommitActivity returned %+v, want %+v", activity, want)
	}

	const methodName = "ListCommitActivity"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListCommitActivity(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListCommitActivity(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_ListCodeFrequency(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/code_frequency", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `[[1302998400, 1124, -435]]`)
	})

	ctx := context.Background()
	code, _, err := client.Repositories.ListCodeFrequency(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListCodeFrequency returned error: %v", err)
	}

	want := []*WeeklyStats{{
		Week:      &Timestamp{time.Date(2011, time.April, 17, 00, 00, 00, 0, time.UTC).Local()},
		Additions: Int(1124),
		Deletions: Int(-435),
	}}

	if !cmp.Equal(code, want) {
		t.Errorf("RepositoriesService.ListCodeFrequency returned %+v, want %+v", code, want)
	}

	const methodName = "ListCodeFrequency"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListCodeFrequency(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListCodeFrequency(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_Participation(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/participation", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `
{
  "all": [
    11,21,15,2,8,1,8,23,17,21,11,10,33,
    91,38,34,22,23,32,3,43,87,71,18,13,5,
    13,16,66,27,12,45,110,117,13,8,18,9,19,
    26,39,12,20,31,46,91,45,10,24,9,29,7
  ],
  "owner": [
    3,2,3,0,2,0,5,14,7,9,1,5,0,
    48,19,2,0,1,10,2,23,40,35,8,8,2,
    10,6,30,0,2,9,53,104,3,3,10,4,7,
    11,21,4,4,22,26,63,11,2,14,1,10,3
  ]
}
`)
	})

	ctx := context.Background()
	participation, _, err := client.Repositories.ListParticipation(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListParticipation returned error: %v", err)
	}

	want := &RepositoryParticipation{
		All: []int{
			11, 21, 15, 2, 8, 1, 8, 23, 17, 21, 11, 10, 33,
			91, 38, 34, 22, 23, 32, 3, 43, 87, 71, 18, 13, 5,
			13, 16, 66, 27, 12, 45, 110, 117, 13, 8, 18, 9, 19,
			26, 39, 12, 20, 31, 46, 91, 45, 10, 24, 9, 29, 7,
		},
		Owner: []int{
			3, 2, 3, 0, 2, 0, 5, 14, 7, 9, 1, 5, 0,
			48, 19, 2, 0, 1, 10, 2, 23, 40, 35, 8, 8, 2,
			10, 6, 30, 0, 2, 9, 53, 104, 3, 3, 10, 4, 7,
			11, 21, 4, 4, 22, 26, 63, 11, 2, 14, 1, 10, 3,
		},
	}

	if !cmp.Equal(participation, want) {
		t.Errorf("RepositoriesService.ListParticipation returned %+v, want %+v", participation, want)
	}

	const methodName = "ListParticipation"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListParticipation(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListParticipation(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_ListPunchCard(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/punch_card", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")

		fmt.Fprint(w, `[
		  [0, 0, 5],
		  [0, 1, 43],
		  [0, 2, 21]
		]`)
	})

	ctx := context.Background()
	card, _, err := client.Repositories.ListPunchCard(ctx, "o", "r")
	if err != nil {
		t.Errorf("RepositoriesService.ListPunchCard returned error: %v", err)
	}

	want := []*PunchCard{
		{Day: Int(0), Hour: Int(0), Commits: Int(5)},
		{Day: Int(0), Hour: Int(1), Commits: Int(43)},
		{Day: Int(0), Hour: Int(2), Commits: Int(21)},
	}

	if !cmp.Equal(card, want) {
		t.Errorf("RepositoriesService.ListPunchCard returned %+v, want %+v", card, want)
	}

	const methodName = "ListPunchCard"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListPunchCard(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListPunchCard(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoriesService_AcceptedError(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stats/contributors", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		// This response indicates the fork will happen asynchronously.
		w.WriteHeader(http.StatusAccepted)
		fmt.Fprint(w, `{"id":1}`)
	})

	ctx := context.Background()
	stats, _, err := client.Repositories.ListContributorsStats(ctx, "o", "r")
	if err == nil {
		t.Errorf("RepositoriesService.AcceptedError should have returned an error")
	}

	if _, ok := err.(*AcceptedError); !ok {
		t.Errorf("RepositoriesService.AcceptedError returned an AcceptedError: %v", err)
	}

	if stats != nil {
		t.Errorf("RepositoriesService.AcceptedError expected stats to be nil: %v", stats)
	}

	const methodName = "ListContributorsStats"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Repositories.ListContributorsStats(ctx, "o", "r")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Repositories.ListContributorsStats(ctx, "o", "r")
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestRepositoryParticipation_Marshal(t *testing.T) {
	testJSONMarshal(t, &RepositoryParticipation{}, "{}")

	u := &RepositoryParticipation{
		All:   []int{1},
		Owner: []int{1},
	}

	want := `{
		"all": [1],
		"owner": [1]
	}`

	testJSONMarshal(t, u, want)
}

func TestWeeklyCommitActivity_Marshal(t *testing.T) {
	testJSONMarshal(t, &WeeklyCommitActivity{}, "{}")

	u := &WeeklyCommitActivity{
		Days:  []int{1},
		Total: Int(1),
		Week:  &Timestamp{referenceTime},
	}

	want := `{
		"days": [
			1
		],
		"total": 1,
		"week": ` + referenceTimeStr + `
	}`

	testJSONMarshal(t, u, want)
}

func TestWeeklyStats_Marshal(t *testing.T) {
	testJSONMarshal(t, &WeeklyStats{}, "{}")

	u := &WeeklyStats{
		Week:      &Timestamp{referenceTime},
		Additions: Int(1),
		Deletions: Int(1),
		Commits:   Int(1),
	}

	want := `{
		"w": ` + referenceTimeStr + `,
		"a": 1,
		"d": 1,
		"c": 1
	}`

	testJSONMarshal(t, u, want)
}

func TestContributorStats_Marshal(t *testing.T) {
	testJSONMarshal(t, &ContributorStats{}, "{}")

	u := &ContributorStats{
		Author: &Contributor{ID: Int64(1)},
		Total:  Int(1),
		Weeks: []*WeeklyStats{
			{
				Week:      &Timestamp{referenceTime},
				Additions: Int(1),
				Deletions: Int(1),
				Commits:   Int(1),
			},
		},
	}

	want := `{
		"author": {
			"id": 1
		},
		"total": 1,
		"weeks": [
			{
				"w": ` + referenceTimeStr + `,
				"a": 1,
				"d": 1,
				"c": 1
			}
		]
	}`

	testJSONMarshal(t, u, want)
}
<|endoftext|>