<commit_before>package main

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"
)

func testcmd(cmd string) (string, error) {
	fmt.Println("(T) " + cmd)
	switch {
	case cmd == "sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir":
		return currenttest.vs.ls(), nil
	case cmd == "docker ps -aq --no-trunc":
		return "", nil
	case strings.HasPrefix(cmd, "docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' "):
		return "", nil
	case strings.HasPrefix(cmd, "sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):
		deleted := cmd[len("sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):]
		deletions = append(deletions, deleted)
		return "", nil
	case strings.HasPrefix(cmd, "sudo readlink /mnt/sda1/var/lib/docker/vfs/dir/"):
		if strings.Contains(cmd, ",nonexistent") {
			return "", errors.New("non-existent linked folder")
		}
		r := regexp.MustCompile(`.*\$([^#]+)###.*`)
		ss := r.FindStringSubmatch(cmd)
		if len(ss) == 2 {
			folder := ss[1]
			folder = folder + strings.Repeat("1", 64-len(folder))
			return folder, nil
		}
		return "", nil
	case strings.HasPrefix(cmd, "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/"):
		if cmd == "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" {
			return "", errors.New("non-ls linked folder")
		}
		return "", nil
	default:
		currentT.Fatalf("test '%s': unknown command!\n", cmd)
		return fmt.Sprintf("test '%s'", cmd), errors.New("unknown command")
	}
}

type volspecs []string
type contspecs []string
type Test struct {
	title string
	vs    volspecs
	cs    contspecs
	res   []int
	strs  []string
}

func newTest(title string) *Test {
	return &Test{title: title, res: []int{0, 0, 0, 0, 0}}
}
func (t *Test) setc(cs contspecs) *Test {
	t.cs = cs
	return t
}

func (vs volspecs) ls() string {
	if len(vs) == 0 {
		return ""
	}
	res := ""
	for i, spec := range vs {
		switch {
		case strings.HasSuffix(spec, "/"):
			spec = spec[:len(spec)-1]
			res = res + spec + strings.Repeat(fmt.Sprintf("%d", i), 64-len(spec)) + "/\n"
		case strings.HasSuffix(spec, "@"):
			mp := "." + strings.Replace(spec, ";", "###", -1)
			mp = strings.Replace(mp, "/", ",#,", -1)
			res = res + mp + "\n"

		default:
			res = res + spec + "\n"
		}
	}
	return res
}

func (cs contspecs) ps() string {
	if len(cs) == 0 {
		return ""
	}
	res := ""
	for _, spec := range cs {
		switch {
		default:
			res = res + spec + "\n"
		}
	}
	return res
}

var deletions = []string{}
var tests = []Test{
	Test{"empty vfs", []string{}, []int{0, 0, 0, 0, 0}, []string{}},
	Test{"two volumes", []string{"fa/", "fb/"}, []int{0, 0, 2, 2, 0}, []string{"vol 'fa00000'<<nil>>", "vol 'fb11111'<<nil>>"}},
	Test{"Invalid (ill-formed) markers must be deleted", []string{"cainv/path/a@"}, []int{0, 0, 0, 0, -1}, []string{}},
	Test{"Invalid (no readlink) markers must be deleted", []string{"ca;/path/nonexistenta@", "cb;/path/nonexistentb@"}, []int{0, 0, 0, 0, -2}, []string{}},
	Test{"Invalid (no ls) markers must be deleted", []string{"ca;/path/nolsa@", "cb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
	Test{"Invalid (no vdir) markers must be deleted", []string{"ca$novdira;/path/nolsa@", "cb$novdirb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
	Test{"two valid markers", []string{"ca$fa;/path/vola@", "cb$fb;/path/volb@"}, []int{0, 0, 0, 0, 2}, []string{"marker 'fa11111'<ca$fa->/path/vola>", "marker 'fb11111'<cb$fb->/path/volb>"}},
	Test{"Invalid (bad name) volume", []string{"inva/"}, []int{0, 0, -1, 0, 0}, []string{}},
	Test{"Invalid file in volume vfs dir", []string{"invf"}, []int{0, 0, -1, 0, 0}, []string{}},
}
var currenttest *Test
var currentT *testing.T

// TestContainers test different vfs scenarios
func TestContainers(t *testing.T) {
	cmd = testcmd
	currentT = t
	for i, test := range tests {
		currenttest = test
		deletions = []string{}
		fmt.Println("------ vvv " + test.title + " vvv ------")
		main()
		tc := Containers()
		toc := OrphanedContainers()
		tv := Volumes()
		tov := OrphanedVolumes()
		tm := Markers()
		if len(tc) != test.res[0] {
			t.Errorf("Test %d: '%s' expected '%d' containers, got '%d'", i+1, test.title, test.res[0], len(tc))
		}
		if len(toc) != test.res[1] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned containers, got '%d'", i+1, test.title, test.res[1], len(toc))
		}
		if nbvolumes(tv) != test.res[2] {
			t.Errorf("Test %d: '%s' expected '%d' volumes, got '%d'", i+1, test.title, test.res[2], nbvolumes(tv))
		}
		if len(tov) != test.res[3] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned volumes, got '%d'", i+1, test.title, test.res[3], len(tov))
		}
		if nbmarkers(tm) != test.res[4] {
			t.Errorf("Test %d: '%s' expected '%d' markers, got '%d'", i+1, test.title, test.res[4], nbmarkers(tm))
		}

		for _, v := range tv {
			vs := v.String()
			check(vs, "volume", test, t, i)
		}
		for _, m := range tm {
			ms := m.String()
			check(ms, "marker", test, t, i)
		}
		fmt.Println("------ ^^^ " + test.title + " ^^^ ------")
		fmt.Println("----------")
	}
}

func check(s string, tmsg string, test *Test, t *testing.T, i int) {
	found := false
	for _, tms := range test.strs {
		if s == tms {
			found = true
			break
		}
	}
	if !found {
		t.Errorf("Test %d: '%s' expected %s '%s', not found", i+1, test.title, tmsg, s)
	}

}

func nbmarkers(tm markers) int {
	res := len(tm)
	for _, d := range deletions {
		if strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}

func nbvolumes(vm volumes) int {
	res := len(vm)
	for _, d := range deletions {
		if !strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}
<commit_msg>gcl_test.go: uses contspec.ps() in testcmd() for docker ps -aq<commit_after>package main

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"testing"
)

func testcmd(cmd string) (string, error) {
	fmt.Println("(T) " + cmd)
	switch {
	case cmd == "sudo ls -a1F /mnt/sda1/var/lib/docker/vfs/dir":
		return currenttest.vs.ls(), nil
	case cmd == "docker ps -aq --no-trunc":
		return currenttest.cs.ps(), nil
	case strings.HasPrefix(cmd, "docker inspect -f '{{ .Name }},{{ range $key, $value := .Volumes }}{{ $key }},{{ $value }}##~#{{ end }}' "):
		return "", nil
	case strings.HasPrefix(cmd, "sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):
		deleted := cmd[len("sudo rm /mnt/sda1/var/lib/docker/vfs/dir/"):]
		deletions = append(deletions, deleted)
		return "", nil
	case strings.HasPrefix(cmd, "sudo readlink /mnt/sda1/var/lib/docker/vfs/dir/"):
		if strings.Contains(cmd, ",nonexistent") {
			return "", errors.New("non-existent linked folder")
		}
		r := regexp.MustCompile(`.*\$([^#]+)###.*`)
		ss := r.FindStringSubmatch(cmd)
		if len(ss) == 2 {
			folder := ss[1]
			folder = folder + strings.Repeat("1", 64-len(folder))
			return folder, nil
		}
		return "", nil
	case strings.HasPrefix(cmd, "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/"):
		if cmd == "sudo ls /mnt/sda1/var/lib/docker/vfs/dir/" {
			return "", errors.New("non-ls linked folder")
		}
		return "", nil
	default:
		currentT.Fatalf("test '%s': unknown command!\n", cmd)
		return fmt.Sprintf("test '%s'", cmd), errors.New("unknown command")
	}
}

type volspecs []string
type contspecs []string
type Test struct {
	title string
	vs    volspecs
	cs    contspecs
	res   []int
	strs  []string
}

func newTest(title string) *Test {
	return &Test{title: title, res: []int{0, 0, 0, 0, 0}}
}
func (t *Test) setc(cs contspecs) *Test {
	t.cs = cs
	return t
}

func (vs volspecs) ls() string {
	if len(vs) == 0 {
		return ""
	}
	res := ""
	for i, spec := range vs {
		switch {
		case strings.HasSuffix(spec, "/"):
			spec = spec[:len(spec)-1]
			res = res + spec + strings.Repeat(fmt.Sprintf("%d", i), 64-len(spec)) + "/\n"
		case strings.HasSuffix(spec, "@"):
			mp := "." + strings.Replace(spec, ";", "###", -1)
			mp = strings.Replace(mp, "/", ",#,", -1)
			res = res + mp + "\n"

		default:
			res = res + spec + "\n"
		}
	}
	return res
}

func (cs contspecs) ps() string {
	if len(cs) == 0 {
		return ""
	}
	res := ""
	for _, spec := range cs {
		switch {
		default:
			res = res + spec + "\n"
		}
	}
	return res
}

var deletions = []string{}
var tests = []Test{
	Test{"empty vfs", []string{}, []int{0, 0, 0, 0, 0}, []string{}},
	Test{"two volumes", []string{"fa/", "fb/"}, []int{0, 0, 2, 2, 0}, []string{"vol 'fa00000'<<nil>>", "vol 'fb11111'<<nil>>"}},
	Test{"Invalid (ill-formed) markers must be deleted", []string{"cainv/path/a@"}, []int{0, 0, 0, 0, -1}, []string{}},
	Test{"Invalid (no readlink) markers must be deleted", []string{"ca;/path/nonexistenta@", "cb;/path/nonexistentb@"}, []int{0, 0, 0, 0, -2}, []string{}},
	Test{"Invalid (no ls) markers must be deleted", []string{"ca;/path/nolsa@", "cb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
	Test{"Invalid (no vdir) markers must be deleted", []string{"ca$novdira;/path/nolsa@", "cb$novdirb;/path/nolsb@"}, []int{0, 0, 0, 0, -2}, []string{}},
	Test{"two valid markers", []string{"ca$fa;/path/vola@", "cb$fb;/path/volb@"}, []int{0, 0, 0, 0, 2}, []string{"marker 'fa11111'<ca$fa->/path/vola>", "marker 'fb11111'<cb$fb->/path/volb>"}},
	Test{"Invalid (bad name) volume", []string{"inva/"}, []int{0, 0, -1, 0, 0}, []string{}},
	Test{"Invalid file in volume vfs dir", []string{"invf"}, []int{0, 0, -1, 0, 0}, []string{}},
}
var currenttest *Test
var currentT *testing.T

// TestContainers test different vfs scenarios
func TestContainers(t *testing.T) {
	cmd = testcmd
	currentT = t
	for i, test := range tests {
		currenttest = test
		deletions = []string{}
		fmt.Println("------ vvv " + test.title + " vvv ------")
		main()
		tc := Containers()
		toc := OrphanedContainers()
		tv := Volumes()
		tov := OrphanedVolumes()
		tm := Markers()
		if len(tc) != test.res[0] {
			t.Errorf("Test %d: '%s' expected '%d' containers, got '%d'", i+1, test.title, test.res[0], len(tc))
		}
		if len(toc) != test.res[1] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned containers, got '%d'", i+1, test.title, test.res[1], len(toc))
		}
		if nbvolumes(tv) != test.res[2] {
			t.Errorf("Test %d: '%s' expected '%d' volumes, got '%d'", i+1, test.title, test.res[2], nbvolumes(tv))
		}
		if len(tov) != test.res[3] {
			t.Errorf("Test %d: '%s' expected '%d' orphaned volumes, got '%d'", i+1, test.title, test.res[3], len(tov))
		}
		if nbmarkers(tm) != test.res[4] {
			t.Errorf("Test %d: '%s' expected '%d' markers, got '%d'", i+1, test.title, test.res[4], nbmarkers(tm))
		}

		for _, v := range tv {
			vs := v.String()
			check(vs, "volume", test, t, i)
		}
		for _, m := range tm {
			ms := m.String()
			check(ms, "marker", test, t, i)
		}
		fmt.Println("------ ^^^ " + test.title + " ^^^ ------")
		fmt.Println("----------")
	}
}

func check(s string, tmsg string, test *Test, t *testing.T, i int) {
	found := false
	for _, tms := range test.strs {
		if s == tms {
			found = true
			break
		}
	}
	if !found {
		t.Errorf("Test %d: '%s' expected %s '%s', not found", i+1, test.title, tmsg, s)
	}

}

func nbmarkers(tm markers) int {
	res := len(tm)
	for _, d := range deletions {
		if strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}

func nbvolumes(vm volumes) int {
	res := len(vm)
	for _, d := range deletions {
		if !strings.HasPrefix(d, ".") {
			res = res - 1
		}
	}
	return res
}
<|endoftext|>


<commit_before>/* Copyright (C) 2015-2022 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2022년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package xt

import (
	"bytes"
	"encoding/binary"
	"github.com/ghts/ghts/lib"
	"strconv"

	"time"
)

type T1404_관리종목_조회_질의값 struct {
	*lib.S질의값_기본형
	M시장_구분    lib.T시장구분
	M관리_질의_구분 T관리_질의_구분
	M연속키      string
}

type T1404_관리종목_조회_응답 struct {
	M헤더     *T1404_관리종목_조회_응답_헤더
	M반복값_모음 *T1404_관리종목_조회_응답_반복값_모음
}

func (s *T1404_관리종목_조회_응답) G헤더_TR데이터() I헤더_TR데이터 {
	return s.M헤더
}

func (s *T1404_관리종목_조회_응답) G반복값_TR데이터() I반복값_모음_TR데이터 {
	return s.M반복값_모음
}

type T1404_관리종목_조회_응답_헤더 struct {
	M연속키 string
}

func (s *T1404_관리종목_조회_응답_헤더) G헤더_TR데이터() I헤더_TR데이터 {
	return s
}

type T1404_관리종목_조회_응답_반복값_모음 struct {
	M배열 []*T1404_관리종목_조회_응답_반복값
}

func (s *T1404_관리종목_조회_응답_반복값_모음) G반복값_모음_TR데이터() I반복값_모음_TR데이터 {
	return s
}

type T1404_관리종목_조회_응답_반복값 struct {
	M종목코드     string
	M종목명      string
	M현재가      int64
	M전일대비구분   T전일대비_구분
	M전일대비_등락폭 int64
	M전일대비_등락율 float64
	M거래량      int64
	M지정일_주가   int64
	//M지정일_대비_등락폭 int64
	//M지정일_대비_등락율 float64
	M사유  T관리종목_지정_사유_구분
	M지정일 time.Time
	M해제일 time.Time
}

func NewT1404InBlock(질의값 *T1404_관리종목_조회_질의값) (g *T1404InBlock) {
	g = new(T1404InBlock)
	lib.F바이트_복사_문자열(g.Gubun[:], strconv.Itoa(int(질의값.M시장_구분)))
	lib.F바이트_복사_문자열(g.Jongchk[:], strconv.Itoa(int(질의값.M관리_질의_구분)))
	lib.F바이트_복사_문자열(g.Shcode[:], 질의값.M연속키)

	f속성값_초기화(g)

	return g
}

func NewT1404_관리종목_조회_응답_헤더(b []byte) (값 *T1404_관리종목_조회_응답_헤더, 에러 error) {
	defer lib.S예외처리{M에러: &에러, M함수: func() { 값 = nil }}.S실행()

	lib.F조건부_패닉(len(b) != SizeT1404OutBlock,
		"예상하지 못한 길이 : '%v", len(b))

	g := new(T1404OutBlock)
	lib.F확인1(binary.Read(bytes.NewBuffer(b), binary.BigEndian, g)) // 네트워크 전송 바이트 순서는 빅엔디언.

	값 = new(T1404_관리종목_조회_응답_헤더)
	값.M연속키 = lib.F2문자열(g.Shcode)

	return 값, nil
}

func NewT1404_관리종목_조회_응답_반복값_모음(b []byte) (값_모음 *T1404_관리종목_조회_응답_반복값_모음, 에러 error) {
	defer lib.S예외처리{M에러: &에러, M함수: func() { 값_모음 = nil }}.S실행()

	나머지 := len(b) % SizeT1404OutBlock1
	lib.F조건부_패닉(나머지 != 0, "예상하지 못한 길이. '%v' '%v'", len(b), 나머지)

	버퍼 := bytes.NewBuffer(b)
	수량 := len(b) / SizeT1404OutBlock1
	g_모음 := make([]*T1404OutBlock1, 수량, 수량)

	값_모음 = new(T1404_관리종목_조회_응답_반복값_모음)
	값_모음.M배열 = make([]*T1404_관리종목_조회_응답_반복값, 수량, 수량)

	for i, g := range g_모음 {
		g = new(T1404OutBlock1)
		lib.F확인1(binary.Read(버퍼, binary.BigEndian, g)) // 네트워크 전송 바이트 순서는 빅엔디언.

		값 := new(T1404_관리종목_조회_응답_반복값)
		값.M종목코드 = lib.F2문자열(g.Shcode)
		값.M종목명 = lib.F2문자열_EUC_KR_공백제거(g.Hname)
		값.M현재가 = lib.F확인2(lib.F2정수64(g.Price))
		값.M전일대비구분 = T전일대비_구분(lib.F확인2(lib.F2정수64(g.Sign)))
		값.M전일대비_등락폭 = 값.M전일대비구분.G부호보정_정수64(lib.F확인2(lib.F2정수64(g.Change)))
		값.M전일대비_등락율 = 값.M전일대비구분.G부호보정_실수64(lib.F확인2(lib.F2실수_소숫점_추가(g.Diff, 2)))
		값.M거래량 = lib.F확인2(lib.F2정수64(g.Volume))
		값.M지정일 = lib.F확인2(lib.F2포맷된_일자("20060102", g.Date))
		값.M지정일_주가 = lib.F확인2(lib.F2정수64(g.Tprice))
		//값.M지정일_대비_등락폭 = lib.F확인2(lib.F2정수64(g.Tchange)
		//값.M지정일_대비_등락율 = lib.F확인2(lib.F2실수_소숫점_추가(g.Tdiff, 2)
		값.M사유 = T관리종목_지정_사유_구분(lib.F확인2(lib.F2정수64(g.Reason)))
		값.M해제일 = lib.F2포맷된_일자_단순형_공백은_초기값("20060102", g.Edate)

		값_모음.M배열[i] = 값
	}

	return 값_모음, nil
}
<commit_msg>t1404 에러 수정.<commit_after>/* Copyright (C) 2015-2022 김운하 (unha.kim@ghts.org)

이 파일은 GHTS의 일부입니다.

이 프로그램은 자유 소프트웨어입니다.
소프트웨어의 피양도자는 자유 소프트웨어 재단이 공표한 GNU LGPL 2.1판
규정에 따라 프로그램을 개작하거나 재배포할 수 있습니다.

이 프로그램은 유용하게 사용될 수 있으리라는 희망에서 배포되고 있지만,
특정한 목적에 적합하다거나, 이익을 안겨줄 수 있다는 묵시적인 보증을 포함한
어떠한 형태의 보증도 제공하지 않습니다.
보다 자세한 사항에 대해서는 GNU LGPL 2.1판을 참고하시기 바랍니다.
GNU LGPL 2.1판은 이 프로그램과 함께 제공됩니다.
만약, 이 문서가 누락되어 있다면 자유 소프트웨어 재단으로 문의하시기 바랍니다.
(자유 소프트웨어 재단 : Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA)

Copyright (C) 2015-2022년 UnHa Kim (unha.kim@ghts.org)

This file is part of GHTS.

GHTS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, version 2.1 of the License.

GHTS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GHTS.  If not, see <http://www.gnu.org/licenses/>. */

package xt

import (
	"bytes"
	"encoding/binary"
	"github.com/ghts/ghts/lib"
	"strconv"

	"time"
)

type T1404_관리종목_조회_질의값 struct {
	*lib.S질의값_기본형
	M시장_구분    lib.T시장구분
	M관리_질의_구분 T관리_질의_구분
	M연속키      string
}

type T1404_관리종목_조회_응답 struct {
	M헤더     *T1404_관리종목_조회_응답_헤더
	M반복값_모음 *T1404_관리종목_조회_응답_반복값_모음
}

func (s *T1404_관리종목_조회_응답) G헤더_TR데이터() I헤더_TR데이터 {
	return s.M헤더
}

func (s *T1404_관리종목_조회_응답) G반복값_TR데이터() I반복값_모음_TR데이터 {
	return s.M반복값_모음
}

type T1404_관리종목_조회_응답_헤더 struct {
	M연속키 string
}

func (s *T1404_관리종목_조회_응답_헤더) G헤더_TR데이터() I헤더_TR데이터 {
	return s
}

type T1404_관리종목_조회_응답_반복값_모음 struct {
	M배열 []*T1404_관리종목_조회_응답_반복값
}

func (s *T1404_관리종목_조회_응답_반복값_모음) G반복값_모음_TR데이터() I반복값_모음_TR데이터 {
	return s
}

type T1404_관리종목_조회_응답_반복값 struct {
	M종목코드     string
	M종목명      string
	M현재가      int64
	M전일대비구분   T전일대비_구분
	M전일대비_등락폭 int64
	M전일대비_등락율 float64
	M거래량      int64
	M지정일_주가   int64
	//M지정일_대비_등락폭 int64
	//M지정일_대비_등락율 float64
	M사유  T관리종목_지정_사유_구분
	M지정일 time.Time
	M해제일 time.Time
}

func NewT1404InBlock(질의값 *T1404_관리종목_조회_질의값) (g *T1404InBlock) {
	g = new(T1404InBlock)
	lib.F바이트_복사_문자열(g.Gubun[:], strconv.Itoa(int(질의값.M시장_구분)))
	lib.F바이트_복사_문자열(g.Jongchk[:], strconv.Itoa(int(질의값.M관리_질의_구분)))
	lib.F바이트_복사_문자열(g.Shcode[:], 질의값.M연속키)

	f속성값_초기화(g)

	return g
}

func NewT1404_관리종목_조회_응답_헤더(b []byte) (값 *T1404_관리종목_조회_응답_헤더, 에러 error) {
	defer lib.S예외처리{M에러: &에러, M함수: func() { 값 = nil }}.S실행()

	lib.F조건부_패닉(len(b) != SizeT1404OutBlock,
		"예상하지 못한 길이 : '%v", len(b))

	g := new(T1404OutBlock)
	lib.F확인1(binary.Read(bytes.NewBuffer(b), binary.BigEndian, g)) // 네트워크 전송 바이트 순서는 빅엔디언.

	값 = new(T1404_관리종목_조회_응답_헤더)
	값.M연속키 = lib.F2문자열(g.Shcode)

	return 값, nil
}

func NewT1404_관리종목_조회_응답_반복값_모음(b []byte) (값_모음 *T1404_관리종목_조회_응답_반복값_모음, 에러 error) {
	defer lib.S예외처리{M에러: &에러, M함수: func() { 값_모음 = nil }}.S실행()

	나머지 := len(b) % SizeT1404OutBlock1
	lib.F조건부_패닉(나머지 != 0, "예상하지 못한 길이. '%v' '%v'", len(b), 나머지)

	버퍼 := bytes.NewBuffer(b)
	수량 := len(b) / SizeT1404OutBlock1
	g_모음 := make([]*T1404OutBlock1, 수량, 수량)

	값_모음 = new(T1404_관리종목_조회_응답_반복값_모음)
	값_모음.M배열 = make([]*T1404_관리종목_조회_응답_반복값, 수량, 수량)

	for i, g := range g_모음 {
		g = new(T1404OutBlock1)
		lib.F확인1(binary.Read(버퍼, binary.BigEndian, g)) // 네트워크 전송 바이트 순서는 빅엔디언.

		값 := new(T1404_관리종목_조회_응답_반복값)
		값.M종목코드 = lib.F2문자열(g.Shcode)
		값.M종목명 = lib.F2문자열_EUC_KR_공백제거(g.Hname)
		값.M현재가 = lib.F확인2(lib.F2정수64(g.Price))
		값.M전일대비구분 = T전일대비_구분(lib.F확인2(lib.F2정수64(g.Sign)))
		값.M전일대비_등락폭 = 값.M전일대비구분.G부호보정_정수64(lib.F확인2(lib.F2정수64(g.Change)))
		값.M전일대비_등락율 = 값.M전일대비구분.G부호보정_실수64(lib.F확인2(lib.F2실수_소숫점_추가(g.Diff, 2)))
		값.M거래량 = lib.F확인2(lib.F2정수64(g.Volume))
		값.M지정일 = lib.F확인2(lib.F2포맷된_일자("20060102", g.Date))
		값.M지정일_주가 = lib.F확인2(lib.F2정수64(g.Tprice))
		//값.M지정일_대비_등락폭 = lib.F확인2(lib.F2정수64(g.Tchange)
		//값.M지정일_대비_등락율 = lib.F확인2(lib.F2실수_소숫점_추가(g.Tdiff, 2)
		값.M사유 = T관리종목_지정_사유_구분(lib.F확인2(lib.F2정수64_공백은_0(g.Reason)))
		값.M해제일 = lib.F2포맷된_일자_단순형_공백은_초기값("20060102", g.Edate)

		값_모음.M배열[i] = 값
	}

	return 값_모음, nil
}
<|endoftext|>


<commit_before>package resingo

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
)

//AuthType is the authentication type that is used to authenticate with a
//resin.io api.
type AuthType int

// supported authentication types
const (
	Credentials AuthType = iota
	AuthToken
)

const (
	pineEndpoint           = "https://api.resin.io/ewa"
	apiEndpoint            = "https://api.resin.io"
	tokenRefreshInterval   = 3600000
	imageCacheTime         = 86400000
	applicationEndpoint    = "/application"
	deviceEndpoint         = "/device"
	keysEndpoint           = "/user__has__public_key"
	applicationEnvEndpoint = "/environment_variable"
	deviceEnvEndpoint      = "/device_environment_variable"
)

//APIVersion is the version of resin API
type APIVersion int

// supported resin API versions
const (
	VersionOne APIVersion = iota
	VersionTwo
	VersionThree
)

func (v APIVersion) String() string {
	switch v {
	case VersionOne:
		return "v1"
	case VersionTwo:
		return "v2"
	case VersionThree:
		return "v3"
	}
	return ""
}

//ErrUnkownAuthType error returned when the type of authentication is not
//supported.
var ErrUnkownAuthType = errors.New("resingo: unknown authentication type")

//ErrMissingCredentials error returned when either username or password is
//missing
var ErrMissingCredentials = errors.New("resingo: missing credentials( username or password)")

//ErrBadToken error returned when the resin session token is bad.
var ErrBadToken = errors.New("resingo: bad session token")

//HTTPClient is an interface for a http clinet that is used to communicate with
//the resin API
type HTTPClient interface {
	Do(*http.Request) (*http.Response, error)
	Post(url string, bodyTyp string, body io.Reader) (*http.Response, error)
}

//Context holds information necessary to make a call to the resin API
type Context struct {
	Client HTTPClient
	Config *Config
}

//Config is the configuration object for the Client
type Config struct {
	AuthToken     string
	Username      string
	Password      string
	APIKey        string
	tokenClain    *TokenClain
	ResinEndpoint string
	ResinVersion  APIVersion
}

//TokenClain are the values that are encoded into a session token from resin.io.
//
// It embeds jst.StandardClaims, so as to help with Verification of expired
// data. Resin doens't do claim verification :(.
type TokenClain struct {
	Username string `json:"username"`
	UserID   int64  `json:"id"`
	Email    string `json:"email"`
	jwt.StandardClaims
}

// formats a proper url forthe API call. The format is
// /<base_url>/<api_version>/<api_endpoin. The endpoint can be en empty string.
//
//This assumes that the endpoint doesn't start with /
// TODO: handle endpoint that starts with / and base url that ends with /
func apiURL(base string, version APIVersion, endpoint string) string {
	return fmt.Sprintf("%s/%s/%s", base, version, endpoint)
}

//APIEndpoint returns a url that points to the given endpoint. This adds the
//resin.io api host and version.
func (c *Config) APIEndpoint(endpoint string) string {
	return apiURL(c.ResinEndpoint, c.ResinVersion, endpoint)
}

//IsValidToken return true if the token tok is a valid resin session token.
//
// This method ecodes the token. A token that can't be doced is bad token. Any
// token that has expired is also a bad token.
func (c *Config) IsValidToken(tok string) bool {
	tk, err := ParseToken(tok)
	if err != nil {
		return false
	}
	return tk.StandardClaims.ExpiresAt > time.Now().Unix()
}

//ValidToken return true if tok is avalid token
func ValidToken(tok string) bool {
	tk, err := ParseToken(tok)
	if err != nil {
		return false
	}
	return tk.StandardClaims.ExpiresAt > time.Now().Unix()
}

//UserID returns the user id.
func (c *Config) UserID() int64 {
	return c.tokenClain.UserID
}

func authHeader(token string) http.Header {
	h := make(http.Header)
	h.Add("Authorization", "Bearer "+token)
	return h
}

//SaveToken saves token, to the current Configuration object.
func (c *Config) SaveToken(tok string) error {
	tk, err := ParseToken(tok)
	if err != nil {
		return err
	}
	c.tokenClain = tk
	c.AuthToken = tok
	return nil
}

//ParseToken parses the given token and extracts the claims emcode into it. This
//function uses JWT method to parse the token, with verification of claims
//turned off.
func ParseToken(tok string) (*TokenClain, error) {
	p := jwt.Parser{
		SkipClaimsValidation: true,
	}
	tk, _ := p.ParseWithClaims(tok, &TokenClain{}, func(token *jwt.Token) (interface{}, error) {
		return nil, nil
	})
	claims, ok := tk.Claims.(*TokenClain)
	if ok {
		return claims, nil
	}
	return nil, ErrBadToken
}

//Authenticate authenticates the client and returns the Auth token. See Login if
//you want to save the token in the client. This function does not save the
//authentication token and user detals.
func Authenticate(ctx *Context, typ AuthType, authToken ...string) (string, error) {
	loginURL := apiEndpoint + "/login_"
	switch typ {
	case Credentials:
		// Absence of either username or password result in missing creadentials
		// error.
		if ctx.Config.Username == "" || ctx.Config.Password == "" {
			return "", ErrMissingCredentials
		}
		form := url.Values{}
		form.Add("username", ctx.Config.Username)
		form.Add("password", ctx.Config.Password)
		res, err := ctx.Client.Post(loginURL,
			"application/x-www-form-urlencoded",
			strings.NewReader(form.Encode()))
		if err != nil {
			return "", err
		}
		defer func() {
			_ = res.Body.Close()
		}()
		data, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return "", err
		}
		return string(data), nil
	case AuthToken:
		if len(authToken) > 0 {
			tk := authToken[0]
			if ctx.Config.IsValidToken(tk) {
				return tk, nil
			}
			return "", ErrBadToken
		}
		return "", errors.New("resingo: Failed to authenticate missing authToken")
	}
	return "", ErrUnkownAuthType
}

//Login authenticates the contextand stores the session token. This function
//checks the validity of the session token before saving it.
//
// The call to ctx.IsLoged() should return true if the returned error is nil.
func Login(ctx *Context, authTyp AuthType, authToken ...string) error {
	tok, err := Authenticate(ctx, authTyp, authToken...)
	if err != nil {
		return err
	}
	if ctx.Config.IsValidToken(tok) {
		return ctx.Config.SaveToken(tok)
	}
	return errors.New("resingo: Failed to login")
}

//Encode encode properly the request params for use with resin API.
//
// Encode tartegts the filter param, which for some reasom(based on OData) is
// supposed to be $filter and not filter. The value specified by the eq param
// key is combined with the value from the fileter key to produce the $filter
// value string.
//
// Any other url params are encoded by the default encoder from
// url.Values.Encoder.
//TODO: check a better way to encode OData url params.
func Encode(q url.Values) string {
	if q == nil {
		return ""
	}
	var buf bytes.Buffer
	var keys []string
	for k := range q {
		keys = append(keys, k)
	}
	for _, k := range keys {
		switch k {
		case "filter":
			if buf.Len() != 0 {
				_, _ = buf.WriteRune('&')
			}
			v := q.Get("filter")
			_, _ = buf.WriteString("$filter=" + v)
			for _, fk := range keys {
				switch fk {
				case "eq":
					fv := "%20" + fk + "%20" + quote(q.Get(fk))
					_, _ = buf.WriteString(fv)
					q.Del(fk)
				}
			}
			q.Del(k)
		case "expand":
			if buf.Len() != 0 {
				_, _ = buf.WriteRune('&')
			}
			v := q.Get("expand")
			_, _ = buf.WriteString("$expand=" + v)
			q.Del(k)
		}
	}
	e := q.Encode()
	if e != "" {
		if buf.Len() != 0 {
			_, _ = buf.WriteRune('&')
		}
		_, _ = buf.WriteString(e)
	}
	return buf.String()
}

func quote(v string) string {
	ok, _ := strconv.ParseBool(v)
	if ok {
		return v
	}
	_, err := strconv.Atoi(v)
	if err == nil {
		return v
	}
	_, err = strconv.ParseFloat(v, 64)
	if err == nil {
		return v
	}
	return "'" + v + "'"
}
<commit_msg>Refactoring<commit_after>package resingo

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
)

//AuthType is the authentication type that is used to authenticate with a
//resin.io api.
type AuthType int

// supported authentication types
const (
	Credentials AuthType = iota
	AuthToken
)

const (
	pineEndpoint           = "https://api.resin.io/ewa"
	apiEndpoint            = "https://api.resin.io"
	tokenRefreshInterval   = 3600000
	imageCacheTime         = 86400000
	applicationEndpoint    = "/application"
	deviceEndpoint         = "/device"
	keysEndpoint           = "/user__has__public_key"
	applicationEnvEndpoint = "/environment_variable"
	deviceEnvEndpoint      = "/device_environment_variable"
)

//APIVersion is the version of resin API
type APIVersion int

// supported resin API versions
const (
	VersionOne APIVersion = iota
	VersionTwo
	VersionThree
)

func (v APIVersion) String() string {
	switch v {
	case VersionOne:
		return "v1"
	case VersionTwo:
		return "v2"
	case VersionThree:
		return "v3"
	}
	return ""
}

//ErrUnkownAuthType error returned when the type of authentication is not
//supported.
var ErrUnkownAuthType = errors.New("resingo: unknown authentication type")

//ErrMissingCredentials error returned when either username or password is
//missing
var ErrMissingCredentials = errors.New("resingo: missing credentials( username or password)")

//ErrBadToken error returned when the resin session token is bad.
var ErrBadToken = errors.New("resingo: bad session token")

//HTTPClient is an interface for a http clinet that is used to communicate with
//the resin API
type HTTPClient interface {
	Do(*http.Request) (*http.Response, error)
	Post(url string, bodyTyp string, body io.Reader) (*http.Response, error)
}

//Context holds information necessary to make a call to the resin API
type Context struct {
	Client HTTPClient
	Config *Config
}

//Config is the configuration object for the Client
type Config struct {
	AuthToken     string
	Username      string
	Password      string
	APIKey        string
	tokenClain    *TokenClain
	ResinEndpoint string
	ResinVersion  APIVersion
}

//TokenClain are the values that are encoded into a session token from resin.io.
//
// It embeds jst.StandardClaims, so as to help with Verification of expired
// data. Resin doens't do claim verification :(.
type TokenClain struct {
	Username string `json:"username"`
	UserID   int64  `json:"id"`
	Email    string `json:"email"`
	jwt.StandardClaims
}

// formats a proper url forthe API call. The format is
// /<base_url>/<api_version>/<api_endpoin. The endpoint can be en empty string.
//
//This assumes that the endpoint doesn't start with /
// TODO: handle endpoint that starts with / and base url that ends with /
func apiURL(base string, version APIVersion, endpoint string) string {
	return fmt.Sprintf("%s/%s/%s", base, version, endpoint)
}

//APIEndpoint returns a url that points to the given endpoint. This adds the
//resin.io api host and version.
func (c *Config) APIEndpoint(endpoint string) string {
	return apiURL(c.ResinEndpoint, c.ResinVersion, endpoint)
}

//IsValidToken return true if the token tok is a valid resin session token.
//
// This method ecodes the token. A token that can't be doced is bad token. Any
// token that has expired is also a bad token.
func (c *Config) IsValidToken(tok string) bool {
	return ValidToken(tok)
}

//ValidToken return true if tok is avalid token
func ValidToken(tok string) bool {
	tk, err := ParseToken(tok)
	if err != nil {
		return false
	}
	return tk.StandardClaims.ExpiresAt > time.Now().Unix()
}

//UserID returns the user id.
func (c *Config) UserID() int64 {
	return c.tokenClain.UserID
}

func authHeader(token string) http.Header {
	h := make(http.Header)
	h.Add("Authorization", "Bearer "+token)
	return h
}

//SaveToken saves token, to the current Configuration object.
func (c *Config) SaveToken(tok string) error {
	tk, err := ParseToken(tok)
	if err != nil {
		return err
	}
	c.tokenClain = tk
	c.AuthToken = tok
	return nil
}

//ParseToken parses the given token and extracts the claims emcode into it. This
//function uses JWT method to parse the token, with verification of claims
//turned off.
func ParseToken(tok string) (*TokenClain, error) {
	p := jwt.Parser{
		SkipClaimsValidation: true,
	}
	tk, _ := p.ParseWithClaims(tok, &TokenClain{}, func(token *jwt.Token) (interface{}, error) {
		return nil, nil
	})
	claims, ok := tk.Claims.(*TokenClain)
	if ok {
		return claims, nil
	}
	return nil, ErrBadToken
}

//Authenticate authenticates the client and returns the Auth token. See Login if
//you want to save the token in the client. This function does not save the
//authentication token and user detals.
func Authenticate(ctx *Context, typ AuthType, authToken ...string) (string, error) {
	loginURL := apiEndpoint + "/login_"
	switch typ {
	case Credentials:
		// Absence of either username or password result in missing creadentials
		// error.
		if ctx.Config.Username == "" || ctx.Config.Password == "" {
			return "", ErrMissingCredentials
		}
		form := url.Values{}
		form.Add("username", ctx.Config.Username)
		form.Add("password", ctx.Config.Password)
		res, err := ctx.Client.Post(loginURL,
			"application/x-www-form-urlencoded",
			strings.NewReader(form.Encode()))
		if err != nil {
			return "", err
		}
		defer func() {
			_ = res.Body.Close()
		}()
		data, err := ioutil.ReadAll(res.Body)
		if err != nil {
			return "", err
		}
		return string(data), nil
	case AuthToken:
		if len(authToken) > 0 {
			tk := authToken[0]
			if ctx.Config.IsValidToken(tk) {
				return tk, nil
			}
			return "", ErrBadToken
		}
		return "", errors.New("resingo: Failed to authenticate missing authToken")
	}
	return "", ErrUnkownAuthType
}

//Login authenticates the contextand stores the session token. This function
//checks the validity of the session token before saving it.
//
// The call to ctx.IsLoged() should return true if the returned error is nil.
func Login(ctx *Context, authTyp AuthType, authToken ...string) error {
	tok, err := Authenticate(ctx, authTyp, authToken...)
	if err != nil {
		return err
	}
	if ctx.Config.IsValidToken(tok) {
		return ctx.Config.SaveToken(tok)
	}
	return errors.New("resingo: Failed to login")
}

//Encode encode properly the request params for use with resin API.
//
// Encode tartegts the filter param, which for some reasom(based on OData) is
// supposed to be $filter and not filter. The value specified by the eq param
// key is combined with the value from the fileter key to produce the $filter
// value string.
//
// Any other url params are encoded by the default encoder from
// url.Values.Encoder.
//TODO: check a better way to encode OData url params.
func Encode(q url.Values) string {
	if q == nil {
		return ""
	}
	var buf bytes.Buffer
	var keys []string
	for k := range q {
		keys = append(keys, k)
	}
	for _, k := range keys {
		switch k {
		case "filter":
			if buf.Len() != 0 {
				_, _ = buf.WriteRune('&')
			}
			v := q.Get("filter")
			_, _ = buf.WriteString("$filter=" + v)
			for _, fk := range keys {
				switch fk {
				case "eq":
					fv := "%20" + fk + "%20" + quote(q.Get(fk))
					_, _ = buf.WriteString(fv)
					q.Del(fk)
				}
			}
			q.Del(k)
		case "expand":
			if buf.Len() != 0 {
				_, _ = buf.WriteRune('&')
			}
			v := q.Get("expand")
			_, _ = buf.WriteString("$expand=" + v)
			q.Del(k)
		}
	}
	e := q.Encode()
	if e != "" {
		if buf.Len() != 0 {
			_, _ = buf.WriteRune('&')
		}
		_, _ = buf.WriteString(e)
	}
	return buf.String()
}

func quote(v string) string {
	ok, _ := strconv.ParseBool(v)
	if ok {
		return v
	}
	_, err := strconv.Atoi(v)
	if err == nil {
		return v
	}
	_, err = strconv.ParseFloat(v, 64)
	if err == nil {
		return v
	}
	return "'" + v + "'"
}
<|endoftext|>


<commit_before>package banana

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"time"

	"runtime"

	"golang.org/x/net/context"
	"gopkg.in/yaml.v2"
)

var routeList map[string][]routeInfo

type AppCfg struct {
	Env struct {
		ConfRoot string
		Port     string
		Level    string
		Tpl      string
		Timeout  time.Duration
	}
}

func checkDir(base, in string) (string, error) {

	if !filepath.IsAbs(in) {
		in = filepath.Join(base, in)
	}
	fi, err := os.Lstat(in)
	if err != nil {
		log.Println(err, base, in)
		return "", err
	}
	if !fi.IsDir() {
		emsg := fmt.Sprintf("%s: should be directory\n", in)
		log.Printf(emsg)
		return "", errors.New(emsg)
	}

	return in, nil
}

func loadCfg(filename string) (cfg AppCfg) {
	filename, err := filepath.Abs(filename)
	if err != nil {
		log.Fatalln("config file path error", err)
	}
	f, err := os.Open(filename)
	if err != nil {
		log.Fatalln("open config file failed", err)
	}
	defer f.Close()
	bf, err := ioutil.ReadAll(f)
	if err != nil {
		log.Fatalln("read config file failed", err)
	}
	err = yaml.Unmarshal(bf, &cfg)
	if err != nil {
		log.Fatalln("load config fail", err)
	}
	cfg.Env.ConfRoot = filepath.Dir(filename)
	cfg.Env.Tpl, err = checkDir(cfg.Env.ConfRoot, cfg.Env.Tpl)
	if err != nil {
		log.Fatalln(err)
	}
	return

}

func parseRule(rule string) (*regexp.Regexp, []string, error) {
	nameList := []string{}
	// 提取字符 key
	re, err := regexp.Compile(":([^/]+)")
	if err != nil {
		log.Panic(err)
		return re, nameList, err
	}
	tmpList := re.FindAllStringSubmatch(rule, -1)
	for _, v := range tmpList {
		// log.Println(v)
		nameList = append(nameList, v[1])
	}
	////log.Println(nameList)
	////log.Println("rule " + rule)
	////log.Println(tmpList)
	////log.Println(re.ReplaceAllString(rule, "([^/]+)"))
	// 构造匹配用的正则
	ruleReg := re.ReplaceAllString(rule, "([^/]+)")
	ruleReg = "^" + ruleReg + "$"
	reg, err := regexp.Compile(ruleReg)
	if err != nil {
		return reg, nameList, err
	}
	return reg, nameList, nil
}

func initial() *MuxContext {
	return bootstrap(flagParams())
}

func bootstrap(confFilename string) *MuxContext {
	runtime.GOMAXPROCS(runtime.NumCPU())
	routeList = make(map[string][]routeInfo)

	cfg := loadCfg(confFilename)

	return &MuxContext{context.WithValue(context.Background(), "cfg", cfg)}
}

func flagParams() (confFilename string) {
	f := flag.NewFlagSet("params", flag.ExitOnError)
	f.StringVar(&confFilename, "c", "./app.yaml", "server configuration")

	if err := f.Parse(os.Args[1:]); err != nil {
		panic(err)
	}
	return
}

func App(args ...string) context.Context {
	var (
		ctx *MuxContext
	)
	if len(args) == 0 {
		ctx = initial()
	} else {
		ctx = bootstrap(args[0])
	}

	go func() {
		err := http.ListenAndServe(":"+ctx.Conf().Env.Port, ctx) //设置监听的端口
		if err != nil {
			log.Print(err)
		}
	}()
	return ctx
}

func Put(pattern string, fn ControllerType) {
	add("PUT", pattern, fn)
}

func Get(pattern string, fn ControllerType) {
	add("GET", pattern, fn)
}

func Post(pattern string, fn ControllerType) {
	add("POST", pattern, fn)
}

func Delete(pattern string, fn ControllerType) {
	add("DELETE", pattern, fn)
}

func Option(pattern string, fn ControllerType) {
	add("OPTION", pattern, fn)
}

func All(pattern string, fn ControllerType) {
	add("GET", pattern, fn)
	add("POST", pattern, fn)
	add("DELETE", pattern, fn)
	add("PUT", pattern, fn)
	add("OPTION", pattern, fn)
	add("HEAD", pattern, fn)
}

func File(prefix string, dir string) {
	fsfn := http.StripPrefix(prefix, http.FileServer(http.Dir(dir))).ServeHTTP
	method := "GET"
	_, exist := routeList[method]
	if !exist {
		routeList[method] = []routeInfo{}
	}

	fn := func(ctx Context) {
		w := ctx.Res()
		r := ctx.Req()

		fsfn(w, r)
	}

	nameList := []string{}
	// 提取字符 key
	ruleReg := "^" + prefix
	reg, err := regexp.Compile(ruleReg)
	if err != nil {
		return
	}
	rInfo := routeInfo{regex: reg, controller: fn, nameList: nameList}
	routeList[method] = append(routeList[method], rInfo)
}
func add(method, pattern string, fn ControllerType) {

	reg, nameList, err := parseRule(pattern)
	if err != nil {
		log.Fatal(err)
		return
	}
	rInfo := routeInfo{regex: reg, controller: fn, nameList: nameList}

	_, exist := routeList[method]
	if !exist {
		routeList[method] = []routeInfo{}
	}

	routeList[method] = append(routeList[method], rInfo)
}

type routeInfo struct {
	regex      *regexp.Regexp
	controller ControllerType
	nameList   []string
}

type ControllerType func(ctx Context)

type controllerType func(http.ResponseWriter, *http.Request)

type MuxContext struct {
	context.Context
}

func (p *MuxContext) Conf() AppCfg {
	cfg, _ := p.Value("cfg").(AppCfg)
	return cfg
}

func (p *MuxContext) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	list, exist := routeList[r.Method]
	if !exist {
		http.NotFound(w, r)
		return
	}

	var (
		ctx     context.Context
		timeout bool = true
	)
	ctx, cancel := context.WithTimeout(p, p.Conf().Env.Timeout*time.Millisecond)
	defer cancel()

	for _, v := range list {
		res := v.regex.FindStringSubmatch(r.URL.Path)

		params := make(map[string]string)
		for k, v := range v.nameList {
			if len(res) > k+1 {
				params[v] = res[k+1]
			} else {
				params[v] = ""
			}
		}
		if len(res) > 0 {
			go func() {
				v.controller(WithHttp(ctx, w, r, params))
				timeout = false
				cancel()
			}()
			break
		}
	}
	<-ctx.Done()
	if timeout {
		w.WriteHeader(http.StatusGatewayTimeout)
	}
	return
}
<commit_msg>for infinite process<commit_after>package banana

import (
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"time"

	"runtime"

	"golang.org/x/net/context"
	"gopkg.in/yaml.v2"
)

var routeList map[string][]routeInfo

type AppCfg struct {
	Env struct {
		ConfRoot string
		Port     string
		Level    string
		Tpl      string
		Timeout  time.Duration
	}
}

func checkDir(base, in string) (string, error) {

	if !filepath.IsAbs(in) {
		in = filepath.Join(base, in)
	}
	fi, err := os.Lstat(in)
	if err != nil {
		log.Println(err, base, in)
		return "", err
	}
	if !fi.IsDir() {
		emsg := fmt.Sprintf("%s: should be directory\n", in)
		log.Printf(emsg)
		return "", errors.New(emsg)
	}

	return in, nil
}

func loadCfg(filename string) (cfg AppCfg) {
	filename, err := filepath.Abs(filename)
	if err != nil {
		log.Fatalln("config file path error", err)
	}
	f, err := os.Open(filename)
	if err != nil {
		log.Fatalln("open config file failed", err)
	}
	defer f.Close()
	bf, err := ioutil.ReadAll(f)
	if err != nil {
		log.Fatalln("read config file failed", err)
	}
	err = yaml.Unmarshal(bf, &cfg)
	if err != nil {
		log.Fatalln("load config fail", err)
	}
	cfg.Env.ConfRoot = filepath.Dir(filename)
	cfg.Env.Tpl, err = checkDir(cfg.Env.ConfRoot, cfg.Env.Tpl)
	if err != nil {
		log.Fatalln(err)
	}
	return

}

func parseRule(rule string) (*regexp.Regexp, []string, error) {
	nameList := []string{}
	// 提取字符 key
	re, err := regexp.Compile(":([^/]+)")
	if err != nil {
		log.Panic(err)
		return re, nameList, err
	}
	tmpList := re.FindAllStringSubmatch(rule, -1)
	for _, v := range tmpList {
		// log.Println(v)
		nameList = append(nameList, v[1])
	}
	////log.Println(nameList)
	////log.Println("rule " + rule)
	////log.Println(tmpList)
	////log.Println(re.ReplaceAllString(rule, "([^/]+)"))
	// 构造匹配用的正则
	ruleReg := re.ReplaceAllString(rule, "([^/]+)")
	ruleReg = "^" + ruleReg + "$"
	reg, err := regexp.Compile(ruleReg)
	if err != nil {
		return reg, nameList, err
	}
	return reg, nameList, nil
}

func initial() *MuxContext {
	return bootstrap(flagParams())
}

func bootstrap(confFilename string) *MuxContext {
	runtime.GOMAXPROCS(runtime.NumCPU())
	routeList = make(map[string][]routeInfo)

	cfg := loadCfg(confFilename)

	return &MuxContext{context.WithValue(context.Background(), "cfg", cfg)}
}

func flagParams() (confFilename string) {
	f := flag.NewFlagSet("params", flag.ExitOnError)
	f.StringVar(&confFilename, "c", "./app.yaml", "server configuration")

	if err := f.Parse(os.Args[1:]); err != nil {
		panic(err)
	}
	return
}

func App(args ...string) context.Context {
	var (
		ctx *MuxContext
	)
	if len(args) == 0 {
		ctx = initial()
	} else {
		ctx = bootstrap(args[0])
	}

	go func() {
		err := http.ListenAndServe(":"+ctx.Conf().Env.Port, ctx) //设置监听的端口
		if err != nil {
			log.Print(err)
		}
	}()
	return ctx
}

func Put(pattern string, fn ControllerType) {
	add("PUT", pattern, fn)
}

func Get(pattern string, fn ControllerType) {
	add("GET", pattern, fn)
}

func Post(pattern string, fn ControllerType) {
	add("POST", pattern, fn)
}

func Delete(pattern string, fn ControllerType) {
	add("DELETE", pattern, fn)
}

func Option(pattern string, fn ControllerType) {
	add("OPTION", pattern, fn)
}

func All(pattern string, fn ControllerType) {
	add("GET", pattern, fn)
	add("POST", pattern, fn)
	add("DELETE", pattern, fn)
	add("PUT", pattern, fn)
	add("OPTION", pattern, fn)
	add("HEAD", pattern, fn)
}

func File(prefix string, dir string) {
	fsfn := http.StripPrefix(prefix, http.FileServer(http.Dir(dir))).ServeHTTP
	method := "GET"
	_, exist := routeList[method]
	if !exist {
		routeList[method] = []routeInfo{}
	}

	fn := func(ctx Context) {
		w := ctx.Res()
		r := ctx.Req()

		fsfn(w, r)
	}

	nameList := []string{}
	// 提取字符 key
	ruleReg := "^" + prefix
	reg, err := regexp.Compile(ruleReg)
	if err != nil {
		return
	}
	rInfo := routeInfo{regex: reg, controller: fn, nameList: nameList}
	routeList[method] = append(routeList[method], rInfo)
}
func add(method, pattern string, fn ControllerType) {

	reg, nameList, err := parseRule(pattern)
	if err != nil {
		log.Fatal(err)
		return
	}
	rInfo := routeInfo{regex: reg, controller: fn, nameList: nameList}

	_, exist := routeList[method]
	if !exist {
		routeList[method] = []routeInfo{}
	}

	routeList[method] = append(routeList[method], rInfo)
}

type routeInfo struct {
	regex      *regexp.Regexp
	controller ControllerType
	nameList   []string
}

type ControllerType func(ctx Context)

type controllerType func(http.ResponseWriter, *http.Request)

type MuxContext struct {
	context.Context
}

func (p *MuxContext) Conf() AppCfg {
	cfg, _ := p.Value("cfg").(AppCfg)
	return cfg
}

func (p *MuxContext) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	list, exist := routeList[r.Method]
	if !exist {
		http.NotFound(w, r)
		return
	}

	var (
		ctx     context.Context
		timeout bool = true
		cancel  func()
	)
	if p.Conf().Env.Timeout == 0 {
		ctx, cancel = context.WithCancel(p)
	} else {
		ctx, cancel = context.WithTimeout(p, p.Conf().Env.Timeout*time.Millisecond)
	}
	defer cancel()

	for _, v := range list {
		res := v.regex.FindStringSubmatch(r.URL.Path)

		params := make(map[string]string)
		for k, v := range v.nameList {
			if len(res) > k+1 {
				params[v] = res[k+1]
			} else {
				params[v] = ""
			}
		}
		if len(res) > 0 {
			go func() {
				v.controller(WithHttp(ctx, w, r, params))
				timeout = false
				cancel()
			}()
			break
		}
	}
	<-ctx.Done()
	if timeout {
		w.WriteHeader(http.StatusGatewayTimeout)
	}
	return
}
<|endoftext|>


<commit_before>/********************************
*** Multiplexer for Go        ***
*** Code is under MIT license ***
*** Code by CodingFerret      ***
*** github.com/squiidz        ***
*********************************/

package bone

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

// Route content the required information for a valid route
// Path: is the Route URL
// Size: is the length of the path
// Token: is the value of each part of the path, split by /
// Pattern: is content information about the route, if it's have a route variable
// handler: is the handler who handle this route
// Method: define HTTP method on the route
type Route struct {
	Path    string
	Size    int
	Token   token
	Pattern Pattern
	handler http.Handler
	Method  string
}

// token content all value of a spliting route path
// tokens: string value of each token
// size: number of token
type token struct {
	tokens []string
	size   int
}

type ByLength []*Route

func (b ByLength) Len() int {
	return len(b)
}

func (b ByLength) Swap(i int, j int) {
	b[i], b[j] = b[j], b[i]
}

func (b ByLength) Less(i int, j int) bool {
	return b[i].Token.size < b[j].Token.size
}

// Pattern content the required information for the route Pattern
// Exist: check if a variable was declare on the route
// Id: the name of the variable
// Pos: postition of var in the route path
// Value: is the value of the request parameters
type Pattern struct {
	Exist bool
	Id    string
	Pos   int
	Value map[string]string
}

// NewRoute return a pointer to a Route instance and call save() on it
func NewRoute(url string, h http.Handler) *Route {
	r := &Route{Path: url, handler: h}
	r.save()
	return r
}

// Save, set automaticly the the Route.Size and Route.Pattern value
func (r *Route) save() {
	r.Token.tokens = strings.Split(r.Path, "/")
	for i, s := range r.Token.tokens {
		if len(s) >= 1 {
			if s[:1] == ":" {
				r.Pattern.Exist = true
				r.Pattern.Id = s[1:]
				r.Pattern.Pos = i
			}
		}
	}
	r.Pattern.Value = make(map[string]string)
	r.Size = len(r.Path)
	r.Token.size = len(r.Token.tokens)
}

// Info is only used for debugging
func (r *Route) Info() {
	fmt.Printf("Path :         %s\n", r.Path)
	fmt.Printf("Size : 		   %d\n", r.Size)
	fmt.Printf("Have Pattern : %t\n", r.Pattern.Exist)
	fmt.Printf("ID :           %s\n", r.Pattern.Id)
	fmt.Printf("Position :     %d\n", r.Pattern.Pos)
	fmt.Printf("Method :       %s\n", r.Method)
}

// Check if the request match the route Pattern
func (r *Route) Match(path string) (url.Values, bool) {
	ss := strings.Split(path, "/")
	if len(ss) == r.Token.size && ss[r.Token.size-1] != "" {
		if r.Path[:r.Pattern.Pos] == path[:r.Pattern.Pos] {
			uV := url.Values{}
			uV.Add(r.Pattern.Id, ss[r.Pattern.Pos])
			return uV, true
		}
	}
	return nil, false
}

// Check if the request respect the route method if provided.
func (r *Route) MethCheck(req *http.Request) bool {
	if r.Method != "" {
		if req.Method == r.Method {
			return true
		} else {
			return false
		}
	} else {
		return true
	}
}

// Set the route method to Get
func (r *Route) Get() *Route {
	r.Method = "GET"
	return r
}

// Set the route method to Post
func (r *Route) Post() *Route {
	r.Method = "POST"
	return r
}

// Set the route method to Put
func (r *Route) Put() *Route {
	r.Method = "PUT"
	return r
}

// Set the route method to Delete
func (r *Route) Delete() *Route {
	r.Method = "DELETE"
	return r
}

// Set the route method to Head
func (r *Route) Head() *Route {
	r.Method = "HEAD"
	return r
}

// Set the route method to Patch
func (r *Route) Patch() *Route {
	r.Method = "PATCH"
	return r
}

// Set the route method to Options
func (r *Route) Options() *Route {
	r.Method = "OPTIONS"
	return r
}

// Only using this in squiidz/fur package
func (r Route) ServeHTTP(rw http.ResponseWriter, req *http.Request) {

	if r.Method != "" {

		if req.Method == r.Method {
			r.handler.ServeHTTP(rw, req)
		} else {
			http.NotFound(rw, req)
		}

	} else {
		r.handler.ServeHTTP(rw, req)
	}

	// DEBUG r.Info()
}
<commit_msg>Refactoring<commit_after>/********************************
*** Multiplexer for Go        ***
*** Code is under MIT license ***
*** Code by CodingFerret      ***
*** github.com/squiidz        ***
*********************************/

package bone

import (
	"fmt"
	"net/http"
	"net/url"
	"strings"
)

// Route content the required information for a valid route
// Path: is the Route URL
// Size: is the length of the path
// Token: is the value of each part of the path, split by /
// Pattern: is content information about the route, if it's have a route variable
// handler: is the handler who handle this route
// Method: define HTTP method on the route
type Route struct {
	Path    string
	Size    int
	Token   Token
	Pattern Pattern
	handler http.Handler
	Method  string
}

// Token content all value of a spliting route path
// Tokens: string value of each token
// size: number of token
type Token struct {
	Tokens []string
	Size   int
}

type ByLength []*Route

func (b ByLength) Len() int {
	return len(b)
}

func (b ByLength) Swap(i int, j int) {
	b[i], b[j] = b[j], b[i]
}

func (b ByLength) Less(i int, j int) bool {
	return b[i].Token.Size < b[j].Token.Size
}

// Pattern content the required information for the route Pattern
// Exist: check if a variable was declare on the route
// Id: the name of the variable
// Pos: postition of var in the route path
// Value: is the value of the request parameters
type Pattern struct {
	Exist bool
	Id    string
	Pos   int
	Value map[string]string
}

// NewRoute return a pointer to a Route instance and call save() on it
func NewRoute(url string, h http.Handler) *Route {
	r := &Route{Path: url, handler: h}
	r.save()
	return r
}

// Save, set automaticly the the Route.Size and Route.Pattern value
func (r *Route) save() {
	r.Token.Tokens = strings.Split(r.Path, "/")
	for i, s := range r.Token.Tokens {
		if len(s) >= 1 {
			if s[:1] == ":" {
				r.Pattern.Exist = true
				r.Pattern.Id = s[1:]
				r.Pattern.Pos = i
			}
		}
	}
	r.Pattern.Value = make(map[string]string)
	r.Size = len(r.Path)
	r.Token.Size = len(r.Token.Tokens)
}

// Info is only used for debugging
func (r *Route) Info() {
	fmt.Printf("Path :         %s\n", r.Path)
	fmt.Printf("Size : 		   %d\n", r.Size)
	fmt.Printf("Have Pattern : %t\n", r.Pattern.Exist)
	fmt.Printf("ID :           %s\n", r.Pattern.Id)
	fmt.Printf("Position :     %d\n", r.Pattern.Pos)
	fmt.Printf("Method :       %s\n", r.Method)
}

// Check if the request match the route Pattern
func (r *Route) Match(path string) (url.Values, bool) {
	ss := strings.Split(path, "/")
	if len(ss) == r.Token.Size && ss[r.Token.Size-1] != "" {
		if r.Path[:r.Pattern.Pos] == path[:r.Pattern.Pos] {
			uV := url.Values{}
			uV.Add(r.Pattern.Id, ss[r.Pattern.Pos])
			return uV, true
		}
	}
	return nil, false
}

// Check if the request respect the route method if provided.
func (r *Route) MethCheck(req *http.Request) bool {
	if r.Method != "" {
		if req.Method == r.Method {
			return true
		} else {
			return false
		}
	} else {
		return true
	}
}

// Set the route method to Get
func (r *Route) Get() *Route {
	r.Method = "GET"
	return r
}

// Set the route method to Post
func (r *Route) Post() *Route {
	r.Method = "POST"
	return r
}

// Set the route method to Put
func (r *Route) Put() *Route {
	r.Method = "PUT"
	return r
}

// Set the route method to Delete
func (r *Route) Delete() *Route {
	r.Method = "DELETE"
	return r
}

// Set the route method to Head
func (r *Route) Head() *Route {
	r.Method = "HEAD"
	return r
}

// Set the route method to Patch
func (r *Route) Patch() *Route {
	r.Method = "PATCH"
	return r
}

// Set the route method to Options
func (r *Route) Options() *Route {
	r.Method = "OPTIONS"
	return r
}

// Only using this in squiidz/fur package
func (r Route) ServeHTTP(rw http.ResponseWriter, req *http.Request) {

	if r.Method != "" {

		if req.Method == r.Method {
			r.handler.ServeHTTP(rw, req)
		} else {
			http.NotFound(rw, req)
		}

	} else {
		r.handler.ServeHTTP(rw, req)
	}

	// DEBUG r.Info()
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//+build js

// The canvas script is for interacting with a graph in a HTML5 canvas.
// Currently all it does is let the user draw a line with mouse events.
package main

import (
	"math"
	"math/rand"

	"github.com/gopherjs/gopherjs/js"
)

const (
	fillStyle   = "#0099ff"
	strokeStyle = "#dddddd"
	snapLen     = 225
)

var (
	graphCanvas   = js.Global.Get("document").Call("getElementById", "graph-canvas")
	ctx           = graphCanvas.Call("getContext", "2d")
	width, height int
	on            bool
)

type point struct{ x, y int }

func resize(*js.Object) {
	on = false
	width, height = graphCanvas.Get("clientWidth").Int(), graphCanvas.Get("clientHeight").Int()
	graphCanvas.Set("width", width)
	graphCanvas.Set("height", height)
}

func main() {
	resize(nil)
	js.Global.Get("window").Call("addEventListener", "resize", resize)
	canvasRect := graphCanvas.Call("getBoundingClientRect")

	startX, startY := 0, 0
	snap := make([]point, 50)
	for i := range snap {
		snap[i] = point{rand.Intn(width), rand.Intn(height)}
	}

	drawPoints := func() {
		// Snap point
		for _, p := range snap {
			ctx.Call("beginPath")
			ctx.Call("arc", p.x, p.y, 4, 0, 2*math.Pi, false)
			ctx.Set("fillStyle", "#000")
			ctx.Call("fill")
			ctx.Set("lineWidth", 1)
			ctx.Set("strokeStyle", strokeStyle)
			ctx.Call("stroke")
		}
	}
	drawPoints()

	graphCanvas.Set("onmousedown", func(event *js.Object) {
		startX = event.Get("clientX").Int() - canvasRect.Get("left").Int()
		startY = event.Get("clientY").Int() - canvasRect.Get("top").Int()
		for _, p := range snap {
			if dx, dy := startX-p.x, startY-p.y; dx*dx+dy*dy < 100 {
				startX, startY = p.x, p.y
				on = true
				break
			}
		}
	})

	drawLine := func(x1, y1, x2, y2 int) {
		// Line outline
		ctx.Call("beginPath")
		ctx.Call("moveTo", x1, y1)
		ctx.Call("lineTo", x2, y2)
		ctx.Set("lineWidth", 4)
		ctx.Set("strokeStyle", strokeStyle)
		ctx.Call("stroke")

		// Start dot
		ctx.Call("beginPath")
		ctx.Call("arc", x1, y1, 4, 0, 2*math.Pi, false)
		ctx.Set("fillStyle", fillStyle)
		ctx.Call("fill")
		ctx.Set("lineWidth", 1)
		ctx.Set("strokeStyle", strokeStyle)
		ctx.Call("stroke")

		// End dot
		ctx.Call("beginPath")
		ctx.Call("arc", x2, y2, 4, 0, 2*math.Pi, false)
		ctx.Set("fillStyle", fillStyle)
		ctx.Call("fill")
		ctx.Set("lineWidth", 1)
		ctx.Set("strokeStyle", strokeStyle)
		ctx.Call("stroke")

		// Line
		ctx.Call("beginPath")
		ctx.Call("moveTo", x1, y1)
		ctx.Call("lineTo", x2, y2)
		ctx.Set("lineWidth", 2)
		ctx.Set("strokeStyle", fillStyle)
		ctx.Call("stroke")
	}

	graphCanvas.Set("onmousemove", func(event *js.Object) {
		x := event.Get("clientX").Int() - canvasRect.Get("left").Int()
		y := event.Get("clientY").Int() - canvasRect.Get("top").Int()
		if !on {
			return
		}

		ctx.Call("clearRect", 0, 0, width, height)
		drawPoints()
		for _, p := range snap {
			if dx, dy := x-p.x, y-p.y; dx*dx+dy*dy < snapLen {
				drawLine(startX, startY, p.x, p.y)
				return
			}
		}
		drawLine(startX, startY, x, y)
	})

	graphCanvas.Set("onmouseup", func(event *js.Object) {
		x := event.Get("clientX").Int() - canvasRect.Get("left").Int()
		y := event.Get("clientY").Int() - canvasRect.Get("top").Int()
		if !on {
			return
		}
		ctx.Call("clearRect", 0, 0, width, height)
		drawPoints()
		for _, p := range snap {
			if dx, dy := x-p.x, y-p.y; -10 < dx && dx < 10 && -10 < dy && dy < 10 {
				drawLine(startX, startY, p.x, p.y)
				break
			}
		}
		on = false
	})
}
<commit_msg>Comment<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//+build js

// The canvas script is for interacting with a graph in a HTML5 canvas.
// Currently all it does is let the user draw a line between random dots.
package main

import (
	"math"
	"math/rand"

	"github.com/gopherjs/gopherjs/js"
)

const (
	fillStyle   = "#0099ff"
	strokeStyle = "#dddddd"
	snapLen     = 225
)

var (
	graphCanvas   = js.Global.Get("document").Call("getElementById", "graph-canvas")
	ctx           = graphCanvas.Call("getContext", "2d")
	width, height int
	on            bool
)

type point struct{ x, y int }

func resize(*js.Object) {
	on = false
	width, height = graphCanvas.Get("clientWidth").Int(), graphCanvas.Get("clientHeight").Int()
	graphCanvas.Set("width", width)
	graphCanvas.Set("height", height)
}

func main() {
	resize(nil)
	js.Global.Get("window").Call("addEventListener", "resize", resize)
	canvasRect := graphCanvas.Call("getBoundingClientRect")

	startX, startY := 0, 0
	snap := make([]point, 50)
	for i := range snap {
		snap[i] = point{rand.Intn(width), rand.Intn(height)}
	}

	drawPoints := func() {
		// Snap point
		for _, p := range snap {
			ctx.Call("beginPath")
			ctx.Call("arc", p.x, p.y, 4, 0, 2*math.Pi, false)
			ctx.Set("fillStyle", "#000")
			ctx.Call("fill")
			ctx.Set("lineWidth", 1)
			ctx.Set("strokeStyle", strokeStyle)
			ctx.Call("stroke")
		}
	}
	drawPoints()

	graphCanvas.Set("onmousedown", func(event *js.Object) {
		startX = event.Get("clientX").Int() - canvasRect.Get("left").Int()
		startY = event.Get("clientY").Int() - canvasRect.Get("top").Int()
		for _, p := range snap {
			if dx, dy := startX-p.x, startY-p.y; dx*dx+dy*dy < 100 {
				startX, startY = p.x, p.y
				on = true
				break
			}
		}
	})

	drawLine := func(x1, y1, x2, y2 int) {
		// Line outline
		ctx.Call("beginPath")
		ctx.Call("moveTo", x1, y1)
		ctx.Call("lineTo", x2, y2)
		ctx.Set("lineWidth", 4)
		ctx.Set("strokeStyle", strokeStyle)
		ctx.Call("stroke")

		// Start dot
		ctx.Call("beginPath")
		ctx.Call("arc", x1, y1, 4, 0, 2*math.Pi, false)
		ctx.Set("fillStyle", fillStyle)
		ctx.Call("fill")
		ctx.Set("lineWidth", 1)
		ctx.Set("strokeStyle", strokeStyle)
		ctx.Call("stroke")

		// End dot
		ctx.Call("beginPath")
		ctx.Call("arc", x2, y2, 4, 0, 2*math.Pi, false)
		ctx.Set("fillStyle", fillStyle)
		ctx.Call("fill")
		ctx.Set("lineWidth", 1)
		ctx.Set("strokeStyle", strokeStyle)
		ctx.Call("stroke")

		// Line
		ctx.Call("beginPath")
		ctx.Call("moveTo", x1, y1)
		ctx.Call("lineTo", x2, y2)
		ctx.Set("lineWidth", 2)
		ctx.Set("strokeStyle", fillStyle)
		ctx.Call("stroke")
	}

	graphCanvas.Set("onmousemove", func(event *js.Object) {
		x := event.Get("clientX").Int() - canvasRect.Get("left").Int()
		y := event.Get("clientY").Int() - canvasRect.Get("top").Int()
		if !on {
			return
		}

		ctx.Call("clearRect", 0, 0, width, height)
		drawPoints()
		for _, p := range snap {
			if dx, dy := x-p.x, y-p.y; dx*dx+dy*dy < snapLen {
				drawLine(startX, startY, p.x, p.y)
				return
			}
		}
		drawLine(startX, startY, x, y)
	})

	graphCanvas.Set("onmouseup", func(event *js.Object) {
		x := event.Get("clientX").Int() - canvasRect.Get("left").Int()
		y := event.Get("clientY").Int() - canvasRect.Get("top").Int()
		if !on {
			return
		}
		ctx.Call("clearRect", 0, 0, width, height)
		drawPoints()
		for _, p := range snap {
			if dx, dy := x-p.x, y-p.y; -10 < dx && dx < 10 && -10 < dy && dy < 10 {
				drawLine(startX, startY, p.x, p.y)
				break
			}
		}
		on = false
	})
}
<|endoftext|>


<commit_before>package torrent

import (
	"github.com/anacrolix/torrent/metainfo"
)

// The public interface for a torrent within a Client.

// A handle to a live torrent within a Client.
type Torrent struct {
	cl *Client
	*torrent
}

func (t Torrent) InfoHash() InfoHash {
	return t.torrent.InfoHash
}

// Closed when the info (.Info()) for the torrent has become available. Using
// features of Torrent that require the info before it is available will have
// undefined behaviour.
func (t *Torrent) GotInfo() <-chan struct{} {
	return t.torrent.gotMetainfo
}

func (t *Torrent) Info() *metainfo.Info {
	return t.torrent.Info
}

// Returns a Reader bound to the torrent's data. All read calls block until
// the data requested is actually available. Priorities are set to ensure the
// data requested will be downloaded as soon as possible.
func (t *Torrent) NewReader() (ret *Reader) {
	ret = &Reader{
		t:         t,
		readahead: 5 * 1024 * 1024,
	}
	return
}

// Returns the state of pieces of the torrent. They are grouped into runs of
// same state. The sum of the state run lengths is the number of pieces
// in the torrent.
func (t *Torrent) PieceStateRuns() []PieceStateRun {
	t.stateMu.Lock()
	defer t.stateMu.Unlock()
	return t.torrent.pieceStateRuns()
}

func (t Torrent) NumPieces() int {
	return t.numPieces()
}

func (t Torrent) Drop() {
	t.cl.mu.Lock()
	t.cl.dropTorrent(t.torrent.InfoHash)
	t.cl.mu.Unlock()
}

func (t Torrent) BytesCompleted() int64 {
	t.cl.mu.RLock()
	defer t.cl.mu.RUnlock()
	return t.bytesCompleted()
}
<commit_msg>Some doc comments<commit_after>package torrent

import (
	"github.com/anacrolix/torrent/metainfo"
)

// This file contains Torrent, until I decide where the private, lower-case
// "torrent" type belongs. That type is currently mostly in torrent.go.

// The public handle to a live torrent within a Client.
type Torrent struct {
	cl *Client
	*torrent
}

// The torrent's infohash. This is fixed and cannot change. It uniquely
// identifies a torrent.
func (t Torrent) InfoHash() InfoHash {
	return t.torrent.InfoHash
}

// Closed when the info (.Info()) for the torrent has become available. Using
// features of Torrent that require the info before it is available will have
// undefined behaviour.
func (t *Torrent) GotInfo() <-chan struct{} {
	return t.torrent.gotMetainfo
}

// Returns the metainfo, or nil if it's not yet available.
func (t *Torrent) Info() *metainfo.Info {
	return t.torrent.Info
}

// Returns a Reader bound to the torrent's data. All read calls block until
// the data requested is actually available. Priorities are set to ensure the
// data requested will be downloaded as soon as possible.
func (t *Torrent) NewReader() (ret *Reader) {
	ret = &Reader{
		t:         t,
		readahead: 5 * 1024 * 1024,
	}
	return
}

// Returns the state of pieces of the torrent. They are grouped into runs of
// same state. The sum of the state run lengths is the number of pieces
// in the torrent.
func (t *Torrent) PieceStateRuns() []PieceStateRun {
	t.stateMu.Lock()
	defer t.stateMu.Unlock()
	return t.torrent.pieceStateRuns()
}

func (t Torrent) NumPieces() int {
	return t.numPieces()
}

// Drop the torrent from the client, and close it.
func (t Torrent) Drop() {
	t.cl.mu.Lock()
	t.cl.dropTorrent(t.torrent.InfoHash)
	t.cl.mu.Unlock()
}

// Number of bytes of the entire torrent we have completed.
func (t Torrent) BytesCompleted() int64 {
	t.cl.mu.RLock()
	defer t.cl.mu.RUnlock()
	return t.bytesCompleted()
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package s3

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/alecthomas/template"
	"github.com/goamz/goamz/aws"
	"github.com/goamz/goamz/s3"
	"github.com/keybase/release/version"
)

type Section struct {
	Header   string
	Releases []Release
}

type Release struct {
	Name    string
	URL     string
	Version string
	Date    string
	Commit  string
}

func NewClient() (client *s3.S3, err error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		return
	}
	client = s3.New(auth, aws.USEast)
	return
}

func WriteHTML(path string, bucketName string, prefixes string, suffix string) error {
	client, err := NewClient()
	if err != nil {

	}
	bucket := client.Bucket(bucketName)
	if bucket == nil {
		return fmt.Errorf("Bucket %s not found", bucketName)
	}

	var sections []Section
	for _, prefix := range strings.Split(prefixes, ",") {
		resp, err := bucket.List(prefix, "", "", 0)
		if err != nil {
			return err
		}

		var releases []Release
		for _, k := range resp.Contents {
			if strings.HasSuffix(k.Key, suffix) {
				key := k.Key
				name := key[len(prefix):]
				urlString := fmt.Sprintf("https://s3.amazonaws.com/%s/%s%s", bucketName, prefix, url.QueryEscape(name))
				version, date, commit, err := version.Parse(name)
				if err != nil {
					log.Printf("Couldn't get version from name: %s\n", name)
				}

				// Convert to Eastern
				locationNewYork, err := time.LoadLocation("America/New_York")
				if err != nil {
					log.Printf("Couldn't load location: %s", err)
				}
				date = date.In(locationNewYork)

				releases = append(releases,
					Release{
						Name:    name,
						URL:     urlString,
						Version: version,
						Date:    date.Format("Mon Jan _2 15:04:05 MST 2006"),
						Commit:  commit,
					})
			}
		}
		sections = append(sections, Section{
			Header:   prefix,
			Releases: reverseRelease(releases),
		})
	}

	return WriteHTMLForLinks(path, bucketName, sections)
}

var htmlTemplate = `
<!doctype html>

  <title>{{ .Title }}</title>

	{{ range $index, $sec := .Sections }}

{{ $sec.Header }}

		{{ range $index2, $rel := $sec.Releases }}

{{ $rel.Name }} {{ $rel.Version }} {{ $rel.Date }} {{ $rel.Commit }}

		{{ end }}

	{{ end }}

`

func WriteHTMLForLinks(path string, title string, sections []Section) error {
	vars := map[string]interface{}{
		"Title":    title,
		"Sections": sections,
	}

	t, err := template.New("t").Parse(htmlTemplate)
	if err != nil {
		return err
	}

	if path != "" {
		var data bytes.Buffer
		err = t.Execute(&data, vars)
		if err != nil {
			return err
		}
		return ioutil.WriteFile(path, data.Bytes(), 0644)
	}
	return nil
}

type Link struct {
	Prefix string
	Suffix string
	Name   string
}

func CopyLatest(bucketName string) error {
	client, err := NewClient()
	if err != nil {
		return err
	}
	bucket := client.Bucket(bucketName)

	linksForPrefix := []Link{
		Link{Prefix: "darwin/", Name: "Keybase.dmg"},
		Link{Prefix: "linux_binaries/deb/", Suffix: "_amd64.deb", Name: "keybase_amd64.deb"},
		Link{Prefix: "linux_binaries/rpm/", Suffix: ".x86_64.rpm", Name: "keybase_amd64.rpm"},
	}

	for _, link := range linksForPrefix {
		resp, err := bucket.List(link.Prefix, "", "", 0)
		if err != nil {
			return err
		}
		keys := reverseKey(resp.Contents)
		for _, k := range keys {
			if !strings.HasSuffix(k.Key, link.Suffix) {
				continue
			}

			url := urlString(k, bucketName, link.Prefix)
			// Instead of linking, we're making copies. S3 linking has some issues.
			// headers := map[string][]string{
			// 	"x-amz-website-redirect-location": []string{url},
			// }
			//err = bucket.PutHeader(name, []byte{}, headers, s3.PublicRead)
			log.Printf("Copying %s from %s (latest)\n", link.Name, k.Key)
			_, err = bucket.PutCopy(link.Name, s3.PublicRead, s3.CopyOptions{}, url)
			if err != nil {
				return err
			}
			break
		}
	}
	return nil
}

func urlString(k s3.Key, bucketName string, prefix string) string {
	key := k.Key
	name := key[len(prefix):]
	return fmt.Sprintf("https://s3.amazonaws.com/%s/%s%s", bucketName, prefix, url.QueryEscape(name))
}

func reverseKey(a []s3.Key) []s3.Key {
	for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
		a[left], a[right] = a[right], a[left]
	}
	return a
}

func reverseRelease(a []Release) []Release {
	for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
		a[left], a[right] = a[right], a[left]
	}
	return a
}
<commit_msg>Fix missing return err<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package s3

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"strings"
	"time"

	"github.com/alecthomas/template"
	"github.com/goamz/goamz/aws"
	"github.com/goamz/goamz/s3"
	"github.com/keybase/release/version"
)

type Section struct {
	Header   string
	Releases []Release
}

type Release struct {
	Name    string
	URL     string
	Version string
	Date    string
	Commit  string
}

func NewClient() (client *s3.S3, err error) {
	auth, err := aws.EnvAuth()
	if err != nil {
		return
	}
	client = s3.New(auth, aws.USEast)
	return
}

func WriteHTML(path string, bucketName string, prefixes string, suffix string) error {
	client, err := NewClient()
	if err != nil {
		return err
	}
	bucket := client.Bucket(bucketName)
	if bucket == nil {
		return fmt.Errorf("Bucket %s not found", bucketName)
	}

	var sections []Section
	for _, prefix := range strings.Split(prefixes, ",") {
		resp, err := bucket.List(prefix, "", "", 0)
		if err != nil {
			return err
		}

		var releases []Release
		for _, k := range resp.Contents {
			if strings.HasSuffix(k.Key, suffix) {
				key := k.Key
				name := key[len(prefix):]
				urlString := fmt.Sprintf("https://s3.amazonaws.com/%s/%s%s", bucketName, prefix, url.QueryEscape(name))
				version, date, commit, err := version.Parse(name)
				if err != nil {
					log.Printf("Couldn't get version from name: %s\n", name)
				}

				// Convert to Eastern
				locationNewYork, err := time.LoadLocation("America/New_York")
				if err != nil {
					log.Printf("Couldn't load location: %s", err)
				}
				date = date.In(locationNewYork)

				releases = append(releases,
					Release{
						Name:    name,
						URL:     urlString,
						Version: version,
						Date:    date.Format("Mon Jan _2 15:04:05 MST 2006"),
						Commit:  commit,
					})
			}
		}
		sections = append(sections, Section{
			Header:   prefix,
			Releases: reverseRelease(releases),
		})
	}

	return WriteHTMLForLinks(path, bucketName, sections)
}

var htmlTemplate = `
<!doctype html>

  <title>{{ .Title }}</title>

	{{ range $index, $sec := .Sections }}

{{ $sec.Header }}

		{{ range $index2, $rel := $sec.Releases }}

{{ $rel.Name }} {{ $rel.Version }} {{ $rel.Date }} {{ $rel.Commit }}

		{{ end }}

	{{ end }}

`

func WriteHTMLForLinks(path string, title string, sections []Section) error {
	vars := map[string]interface{}{
		"Title":    title,
		"Sections": sections,
	}

	t, err := template.New("t").Parse(htmlTemplate)
	if err != nil {
		return err
	}

	if path != "" {
		var data bytes.Buffer
		err = t.Execute(&data, vars)
		if err != nil {
			return err
		}
		return ioutil.WriteFile(path, data.Bytes(), 0644)
	}
	return nil
}

type Link struct {
	Prefix string
	Suffix string
	Name   string
}

func CopyLatest(bucketName string) error {
	client, err := NewClient()
	if err != nil {
		return err
	}
	bucket := client.Bucket(bucketName)

	linksForPrefix := []Link{
		Link{Prefix: "darwin/", Name: "Keybase.dmg"},
		Link{Prefix: "linux_binaries/deb/", Suffix: "_amd64.deb", Name: "keybase_amd64.deb"},
		Link{Prefix: "linux_binaries/rpm/", Suffix: ".x86_64.rpm", Name: "keybase_amd64.rpm"},
	}

	for _, link := range linksForPrefix {
		resp, err := bucket.List(link.Prefix, "", "", 0)
		if err != nil {
			return err
		}
		keys := reverseKey(resp.Contents)
		for _, k := range keys {
			if !strings.HasSuffix(k.Key, link.Suffix) {
				continue
			}

			url := urlString(k, bucketName, link.Prefix)
			// Instead of linking, we're making copies. S3 linking has some issues.
			// headers := map[string][]string{
			// 	"x-amz-website-redirect-location": []string{url},
			// }
			//err = bucket.PutHeader(name, []byte{}, headers, s3.PublicRead)
			log.Printf("Copying %s from %s (latest)\n", link.Name, k.Key)
			_, err = bucket.PutCopy(link.Name, s3.PublicRead, s3.CopyOptions{}, url)
			if err != nil {
				return err
			}
			break
		}
	}
	return nil
}

func urlString(k s3.Key, bucketName string, prefix string) string {
	key := k.Key
	name := key[len(prefix):]
	return fmt.Sprintf("https://s3.amazonaws.com/%s/%s%s", bucketName, prefix, url.QueryEscape(name))
}

func reverseKey(a []s3.Key) []s3.Key {
	for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
		a[left], a[right] = a[right], a[left]
	}
	return a
}

func reverseRelease(a []Release) []Release {
	for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
		a[left], a[right] = a[right], a[left]
	}
	return a
}
<|endoftext|>


<commit_before>package jsoniter

func (iter *Iterator) ReadObject() (ret string) {
	c := iter.nextToken()
	switch c {
	case 'n':
		iter.skipFixedBytes(3)
		return "" // null
	case '{':
		c = iter.nextToken()
		if c == '"' {
			iter.unreadByte()
			return string(iter.readObjectFieldAsBytes())
		}
		if c == '}' {
			return "" // end of object
		}
		iter.reportError("ReadObject", `expect " after {`)
		return
	case ',':
		return string(iter.readObjectFieldAsBytes())
	case '}':
		return "" // end of object
	default:
		iter.reportError("ReadObject", `expect { or , or } or n`)
		return
	}
}

func (iter *Iterator) readFieldHash() int32 {
	hash := 0x811c9dc5
	c := iter.nextToken()
	if c == '"' {
		for {
			for i := iter.head; i < iter.tail; i++ {
				// require ascii string and no escape
				b := iter.buf[i]
				if b == '"' {
					iter.head = i+1
					c = iter.nextToken()
					if c != ':' {
						iter.reportError("readFieldHash", `expect :, but found ` + string([]byte{c}))
					}
					return int32(hash)
				}
				hash ^= int(b)
				hash *= 0x1000193
			}
			if !iter.loadMore() {
				iter.reportError("readFieldHash", `incomplete field name`)
				return 0
			}
		}
	}
	iter.reportError("readFieldHash", `expect ", but found ` + string([]byte{c}))
	return 0
}

func calcHash(str string) int32 {
	hash := 0x811c9dc5
	for _, b := range str {
		hash ^= int(b)
		hash *= 0x1000193
	}
	return int32(hash)
}

func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
	c := iter.nextToken()
	if c == '{' {
		c = iter.nextToken()
		if c == '"' {
			iter.unreadByte()
			field := string(iter.readObjectFieldAsBytes())
			if !callback(iter, field) {
				return false
			}
			for iter.nextToken() == ',' {
				field := string(iter.readObjectFieldAsBytes())
				if !callback(iter, field) {
					return false
				}
			}
			return true
		}
		if c == '}' {
			return true
		}
		iter.reportError("ReadObjectCB", `expect " after }`)
		return false
	}
	if c == 'n' {
		iter.skipFixedBytes(3)
		return true // null
	}
	iter.reportError("ReadObjectCB", `expect { or n`)
	return false
}

func (iter *Iterator) readObjectStart() bool {
	c := iter.nextToken()
	if c == '{' {
		c = iter.nextToken()
		if c == '}' {
			return false
		}
		iter.unreadByte()
		return true
	}
	iter.reportError("readObjectStart", "expect { ")
	return false
}

func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
	str := iter.ReadStringAsSlice()
	if iter.skipWhitespacesWithoutLoadMore() {
		if ret == nil {
			ret = make([]byte, len(str))
			copy(ret, str)
		}
		if !iter.loadMore() {
			return
		}
	}
	if iter.buf[iter.head] != ':' {
		iter.reportError("readObjectFieldAsBytes", "expect : after object field")
		return
	}
	iter.head++
	if iter.skipWhitespacesWithoutLoadMore() {
		if ret == nil {
			ret = make([]byte, len(str))
			copy(ret, str)
		}
		if !iter.loadMore() {
			return
		}
	}
	if ret == nil {
		return str
	}
	return ret
}
<commit_msg>fix issue on 32bit platform<commit_after>package jsoniter

func (iter *Iterator) ReadObject() (ret string) {
	c := iter.nextToken()
	switch c {
	case 'n':
		iter.skipFixedBytes(3)
		return "" // null
	case '{':
		c = iter.nextToken()
		if c == '"' {
			iter.unreadByte()
			return string(iter.readObjectFieldAsBytes())
		}
		if c == '}' {
			return "" // end of object
		}
		iter.reportError("ReadObject", `expect " after {`)
		return
	case ',':
		return string(iter.readObjectFieldAsBytes())
	case '}':
		return "" // end of object
	default:
		iter.reportError("ReadObject", `expect { or , or } or n`)
		return
	}
}

func (iter *Iterator) readFieldHash() int32 {
	hash := int64(0x811c9dc5)
	c := iter.nextToken()
	if c == '"' {
		for {
			for i := iter.head; i < iter.tail; i++ {
				// require ascii string and no escape
				b := iter.buf[i]
				if b == '"' {
					iter.head = i+1
					c = iter.nextToken()
					if c != ':' {
						iter.reportError("readFieldHash", `expect :, but found ` + string([]byte{c}))
					}
					return int32(hash)
				}
				hash ^= int(b)
				hash *= 0x1000193
			}
			if !iter.loadMore() {
				iter.reportError("readFieldHash", `incomplete field name`)
				return 0
			}
		}
	}
	iter.reportError("readFieldHash", `expect ", but found ` + string([]byte{c}))
	return 0
}

func calcHash(str string) int32 {
	hash := int64(0x811c9dc5)
	for _, b := range str {
		hash ^= int(b)
		hash *= 0x1000193
	}
	return int32(hash)
}

func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
	c := iter.nextToken()
	if c == '{' {
		c = iter.nextToken()
		if c == '"' {
			iter.unreadByte()
			field := string(iter.readObjectFieldAsBytes())
			if !callback(iter, field) {
				return false
			}
			for iter.nextToken() == ',' {
				field := string(iter.readObjectFieldAsBytes())
				if !callback(iter, field) {
					return false
				}
			}
			return true
		}
		if c == '}' {
			return true
		}
		iter.reportError("ReadObjectCB", `expect " after }`)
		return false
	}
	if c == 'n' {
		iter.skipFixedBytes(3)
		return true // null
	}
	iter.reportError("ReadObjectCB", `expect { or n`)
	return false
}

func (iter *Iterator) readObjectStart() bool {
	c := iter.nextToken()
	if c == '{' {
		c = iter.nextToken()
		if c == '}' {
			return false
		}
		iter.unreadByte()
		return true
	}
	iter.reportError("readObjectStart", "expect { ")
	return false
}

func (iter *Iterator) readObjectFieldAsBytes() (ret []byte) {
	str := iter.ReadStringAsSlice()
	if iter.skipWhitespacesWithoutLoadMore() {
		if ret == nil {
			ret = make([]byte, len(str))
			copy(ret, str)
		}
		if !iter.loadMore() {
			return
		}
	}
	if iter.buf[iter.head] != ':' {
		iter.reportError("readObjectFieldAsBytes", "expect : after object field")
		return
	}
	iter.head++
	if iter.skipWhitespacesWithoutLoadMore() {
		if ret == nil {
			ret = make([]byte, len(str))
			copy(ret, str)
		}
		if !iter.loadMore() {
			return
		}
	}
	if ret == nil {
		return str
	}
	return ret
}
<|endoftext|>


<commit_before>// The ghissues package provides simple hooks into Github's Issues API
package ghissues

import (
	"http"
	"fmt"
	"json"
	"io/ioutil"
        "os"
)

const base_api_url = "http://github.com/api/v2/json"

type Issue struct {
	Gravatar_id string
	Position float32
	Number int
	Votes int
	Created_at string
	Comments int
	Body string
	Title string
	Updated_at string
	Html_url string
	User string
	Labels []Label
	State string
}

type Comment struct {
	Gravatar_id string
	Created_at string
	Body string
	Updated_at string
	Id int
	User string
}

type Label string

type PullRequest struct { // @@@ Unimplemented
	issue Issue
	pull_request_url string
	html_url string
	patch_url string
}

type IssuesClient struct {
	username string
	token string
	client *http.Client
}

// Responses
type multipleIssueResponse struct {
	Issues []Issue
}
type multipleCommentResponse struct {
	Comments []Comment
}
type singleIssueResponse struct {
	Issue Issue
}
type singleCommentResponse struct {
	Comment Comment
}
type multipleLabelResponse struct {
	Labels []Label
}

func NewClient(username, token string) *IssuesClient {
	return &IssuesClient{username, token, new(http.Client)}
}

func (ic *IssuesClient) post(url string, data map[string]string) (*http.Response, os.Error) {
	if _, username_exists := data["login"]; !username_exists {
		data["login"] = ic.username
	}
	if _, token_exists := data["token"]; !token_exists {
		data["token"] = ic.token
	}
	return ic.client.PostForm(url, data)
}

func (ic *IssuesClient) get(url string) (*http.Response, os.Error) {
	response, _, err := ic.client.Get(url)
	if response.StatusCode != 200 {
		return response, os.NewError(
			fmt.Sprintf("Got a %v status code on fetch of %v.", response.StatusCode, url))
	}
	return response, err
}

func (ic *IssuesClient) parseJson(response *http.Response, toStructure interface{}) (interface{}, os.Error) {
	b, err := ioutil.ReadAll(response.Body)
	if err != nil {
                return toStructure, err
	}
	err2 := json.Unmarshal(b, toStructure)
	return toStructure, err2
}

func (ic *IssuesClient) Search(user, repo, state, term string) ([]Issue, os.Error) {
	url_string := fmt.Sprintf("%v/issues/search/%v/%v/%v/%v/", base_api_url, user, repo, state, term)
	response, err := ic.get(url_string)
	if err != nil {
                return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleIssueResponse))
	return json.(*multipleIssueResponse).Issues, err2
}

func (ic *IssuesClient) List(user, repo, state string) ([]Issue, os.Error) {
	url_string := fmt.Sprintf("%v/issues/list/%v/%v/%v/", base_api_url, user, repo, state)
        response, err := ic.get(url_string)
	if err != nil {
                return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleIssueResponse))
	return json.(*multipleIssueResponse).Issues, err2
}

func (ic *IssuesClient) Create(user, repo, title, body string) (Issue, os.Error) {
	url_string := fmt.Sprintf("%v/issues/open/%v/%v/", base_api_url, user, repo)
	post_data := make(map[string]string){
        "title":title,
        "body":body,
    }
	response, err := ic.post(url_string, post_data)
	if err != nil {
                return Issue{}, err
	}
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) Detail(user, repo string, issueNumber int) (Issue, os.Error) {
	url_string := fmt.Sprintf("%v/issues/show/%v/%v/%v", base_api_url, user, repo, issueNumber)
	response, err := ic.get(url_string)
	if err != nil {
                return Issue{}, err
	}
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) Edit(user, repo string, issueNumber int, title, body string) (Issue, os.Error) {
	url_string := fmt.Sprintf("%v/issues/edit/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	post_data := make(map[string]string){
        "title":title,
        "body":body,
    }
	response, err := ic.post(url_string, post_data)
        if err != nil {
                return Issue{}, err
        }
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) Close(user, repo string, issueNumber int) (Issue, os.Error) {
	url_string := fmt.Sprintf("%v/issues/close/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
        if err != nil {
                return Issue{}, err
        }
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) Reopen(user, repo string, issueNumber int) (Issue, os.Error) {
	url_string := fmt.Sprintf("%v/issues/reopen/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
        if err != nil {
                return Issue{}, err
        }
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) ListComments(user, repo string, issueNumber int) ([]Comment, os.Error) {
	url_string := fmt.Sprintf("%v/issues/comments/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	response, err := ic.get(url_string)
        if err != nil {
                return nil, err
        }
	json, err2 := ic.parseJson(response, new(multipleCommentResponse))
	return json.(*multipleCommentResponse).Comments, err2
}

func (ic *IssuesClient) AddComment(user, repo string, issueNumber int, comment string) (Comment, os.Error) {
	url_string := fmt.Sprintf("%v/issues/comment/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	post_data := make(map[string]string){
        "comment":comment,
    }
	response, err := ic.post(url_string, post_data)
        if err != nil {
                return Comment{}, err
        }
	json, err2 := ic.parseJson(response, new(singleCommentResponse))
	return json.(*singleCommentResponse).Comment, err2
}

func (ic *IssuesClient) ListLabels(user, repo string) ([]Label, os.Error) {
	url_string := fmt.Sprintf("%v/issues/labels/%v/%v/", base_api_url, user, repo)
	response, err := ic.get(url_string)
        if err != nil {
                return nil, err
        }
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}

func (ic *IssuesClient) AddLabelToRepo(user, repo, label string) ([]Label, os.Error) {
	url_string := fmt.Sprintf("%v/issues/label/add/%v/%v/%v/", base_api_url, user, repo, label)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
        if err != nil {
                return nil, err
        }
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}

func (ic *IssuesClient) AddLabelToIssue(user, repo string, issueNumber int, label string) ([]Label, os.Error) {
	url_string := fmt.Sprintf("%v/issues/label/add/%v/%v/%v/%v/", base_api_url, user, repo, label, issueNumber)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
        if err != nil {
                return nil, err
        }
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}

func (ic *IssuesClient) RemoveLabelFromRepo(user, repo, label string) ([]Label, os.Error) {
	url_string := fmt.Sprintf("%v/issues/label/remove/%v/%v/%v/", base_api_url, user, repo, label)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
        if err != nil {
                return nil, err
        }
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}

func (ic *IssuesClient) RemoveLabelFromIssue(user, repo string, issueNumber int, label string) ([]Label, os.Error) {
	url_string := fmt.Sprintf("%v/issues/label/remove/%v/%v/%v/%v/", base_api_url, user, repo, label, issueNumber)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
        if err != nil {
                return nil, err
        }
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}
<commit_msg>Updating for golang v1<commit_after>// The ghissues package provides simple hooks into Github's Issues API
package ghissues

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

const base_api_url = "http://github.com/api/v2/json"

type Issue struct {
	Gravatar_id string
	Position    float32
	Number      int
	Votes       int
	Created_at  string
	Comments    int
	Body        string
	Title       string
	Updated_at  string
	Html_url    string
	User        string
	Labels      []Label
	State       string
}

type Comment struct {
	Gravatar_id string
	Created_at  string
	Body        string
	Updated_at  string
	Id          int
	User        string
}

type Label string

type PullRequest struct { // @@@ Unimplemented
	issue            Issue
	pull_request_url string
	html_url         string
	patch_url        string
}

type IssuesClient struct {
	username string
	token    string
	client   *http.Client
}

// Responses
type multipleIssueResponse struct {
	Issues []Issue
}
type multipleCommentResponse struct {
	Comments []Comment
}
type singleIssueResponse struct {
	Issue Issue
}
type singleCommentResponse struct {
	Comment Comment
}
type multipleLabelResponse struct {
	Labels []Label
}

func NewClient(username, token string) *IssuesClient {
	return &IssuesClient{username, token, new(http.Client)}
}

func (ic *IssuesClient) post(url string, data map[string]string) (*http.Response, error) {
	if _, username_exists := data["login"]; !username_exists {
		data["login"] = ic.username
	}
	if _, token_exists := data["token"]; !token_exists {
		data["token"] = ic.token
	}
	return ic.client.PostForm(url, data)
}

func (ic *IssuesClient) get(url string) (*http.Response, error) {
	response, _, err := ic.client.Get(url)
	if response.StatusCode != 200 {
		return response, errors.New(
			fmt.Sprintf("Got a %v status code on fetch of %v.", response.StatusCode, url))
	}
	return response, err
}

func (ic *IssuesClient) parseJson(response *http.Response, toStructure interface{}) (interface{}, error) {
	b, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return toStructure, err
	}
	err2 := json.Unmarshal(b, toStructure)
	return toStructure, err2
}

func (ic *IssuesClient) Search(user, repo, state, term string) ([]Issue, error) {
	url_string := fmt.Sprintf("%v/issues/search/%v/%v/%v/%v/", base_api_url, user, repo, state, term)
	response, err := ic.get(url_string)
	if err != nil {
		return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleIssueResponse))
	return json.(*multipleIssueResponse).Issues, err2
}

func (ic *IssuesClient) List(user, repo, state string) ([]Issue, error) {
	url_string := fmt.Sprintf("%v/issues/list/%v/%v/%v/", base_api_url, user, repo, state)
	response, err := ic.get(url_string)
	if err != nil {
		return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleIssueResponse))
	return json.(*multipleIssueResponse).Issues, err2
}

func (ic *IssuesClient) Create(user, repo, title, body string) (Issue, error) {
	url_string := fmt.Sprintf("%v/issues/open/%v/%v/", base_api_url, user, repo)
	post_data := make(map[string]string)
	post_data["title"] = title
	post_data["body"] = body
	response, err := ic.post(url_string, post_data)
	if err != nil {
		return Issue{}, err
	}
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) Detail(user, repo string, issueNumber int) (Issue, error) {
	url_string := fmt.Sprintf("%v/issues/show/%v/%v/%v", base_api_url, user, repo, issueNumber)
	response, err := ic.get(url_string)
	if err != nil {
		return Issue{}, err
	}
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) Edit(user, repo string, issueNumber int, title, body string) (Issue, error) {
	url_string := fmt.Sprintf("%v/issues/edit/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	post_data := make(map[string]string)
	post_data["title"] = title
	post_data["body"] = body
	response, err := ic.post(url_string, post_data)
	if err != nil {
		return Issue{}, err
	}
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) Close(user, repo string, issueNumber int) (Issue, error) {
	url_string := fmt.Sprintf("%v/issues/close/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
	if err != nil {
		return Issue{}, err
	}
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) Reopen(user, repo string, issueNumber int) (Issue, error) {
	url_string := fmt.Sprintf("%v/issues/reopen/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
	if err != nil {
		return Issue{}, err
	}
	json, err2 := ic.parseJson(response, new(singleIssueResponse))
	return json.(*singleIssueResponse).Issue, err2
}

func (ic *IssuesClient) ListComments(user, repo string, issueNumber int) ([]Comment, error) {
	url_string := fmt.Sprintf("%v/issues/comments/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	response, err := ic.get(url_string)
	if err != nil {
		return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleCommentResponse))
	return json.(*multipleCommentResponse).Comments, err2
}

func (ic *IssuesClient) AddComment(user, repo string, issueNumber int, comment string) (Comment, error) {
	url_string := fmt.Sprintf("%v/issues/comment/%v/%v/%v/", base_api_url, user, repo, issueNumber)
	post_data := make(map[string]string)
	post_data["comment"] = comment
	response, err := ic.post(url_string, post_data)
	if err != nil {
		return Comment{}, err
	}
	json, err2 := ic.parseJson(response, new(singleCommentResponse))
	return json.(*singleCommentResponse).Comment, err2
}

func (ic *IssuesClient) ListLabels(user, repo string) ([]Label, error) {
	url_string := fmt.Sprintf("%v/issues/labels/%v/%v/", base_api_url, user, repo)
	response, err := ic.get(url_string)
	if err != nil {
		return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}

func (ic *IssuesClient) AddLabelToRepo(user, repo, label string) ([]Label, error) {
	url_string := fmt.Sprintf("%v/issues/label/add/%v/%v/%v/", base_api_url, user, repo, label)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
	if err != nil {
		return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}

func (ic *IssuesClient) AddLabelToIssue(user, repo string, issueNumber int, label string) ([]Label, error) {
	url_string := fmt.Sprintf("%v/issues/label/add/%v/%v/%v/%v/", base_api_url, user, repo, label, issueNumber)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
	if err != nil {
		return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}

func (ic *IssuesClient) RemoveLabelFromRepo(user, repo, label string) ([]Label, error) {
	url_string := fmt.Sprintf("%v/issues/label/remove/%v/%v/%v/", base_api_url, user, repo, label)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
	if err != nil {
		return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}

func (ic *IssuesClient) RemoveLabelFromIssue(user, repo string, issueNumber int, label string) ([]Label, error) {
	url_string := fmt.Sprintf("%v/issues/label/remove/%v/%v/%v/%v/", base_api_url, user, repo, label, issueNumber)
	post_data := make(map[string]string)
	response, err := ic.post(url_string, post_data)
	if err != nil {
		return nil, err
	}
	json, err2 := ic.parseJson(response, new(multipleLabelResponse))
	return json.(*multipleLabelResponse).Labels, err2
}
<|endoftext|>