<commit_before>package main

import (
	"net"
	"os"
	"strings"
	"sync"

	"github.com/cyfdecyf/bufio"
)

type DirectList struct {
	Domain map[string]DomainType
	sync.RWMutex
}

type DomainType byte

const (
	domainTypeUnknown DomainType = iota
	domainTypeDirect
	domainTypeProxy
	domainTypeReject
)
const MAX_URL_DOT = 5

func newDirectList() *DirectList {
	return &DirectList{
		Domain: map[string]DomainType{},
	}
}

func charIndex(url string, c byte) []int {
	indexes := make([]int, 0, MAX_URL_DOT+1)
	n := len(url)

	if n <= 1 {
		return indexes
	}

	for i := 0; i < n-1; i++ {
		if url[i] == c {
			indexes = append(indexes, i)
		}
	}
	return indexes
}

func domainSearch(Domain map[string]DomainType, url string, isIP bool) (domainType DomainType, ok bool) {
	if v, ok := Domain[url]; ok || isIP {
		return v, ok
	}

	indexes := charIndex(url, '.')
	n := len(indexes)
	if n > MAX_URL_DOT {
		indexes = indexes[n-MAX_URL_DOT:]
		n = MAX_URL_DOT
	}

	for i := 0; i < n; i++ {
		url_suffix := url[indexes[i]+1:]
		if v, ok := Domain[url_suffix]; ok {
			return v, ok
		}
	}

	return domainTypeUnknown, false
}

func (directList *DirectList) shouldDirect(url *URL) (domainType DomainType) {
	debug.Printf("judging host: %s", url.Host)

	if parentProxy.empty() { // no way to retry, so always visit directly
		return domainTypeDirect
	}

	if url.Domain == "" { // simple host or private ip
		return domainTypeDirect
	}

	isIP, isPrivate := hostIsIP(url.Host)
	if v, ok := domainSearch(directList.Domain, url.Host, isIP); ok {
		return v
	}

	if !config.JudgeByIP {
		return domainTypeProxy
	}

	var ip string
	if isIP {
		if isPrivate {
			directList.add(url.Host, domainTypeDirect)
			return domainTypeDirect
		}
		ip = url.Host
	} else {
		hostIPs, err := net.LookupIP(url.Host)
		if err != nil {
			errl.Printf("error looking up host ip %s, err %s", url.Host, err)
			return domainTypeProxy
		}
		ip = hostIPs[0].String()
	}

	if ipShouldDirect(ip) {
		directList.add(url.Host, domainTypeDirect)
		return domainTypeDirect
	} else {
		directList.add(url.Host, domainTypeProxy)
		return domainTypeProxy
	}
}

func (directList *DirectList) add(host string, domainType DomainType) {
	directList.Lock()
	defer directList.Unlock()
	directList.Domain[host] = domainType
}

func (directList *DirectList) GetDirectList() []string {
	lst := make([]string, 0)
	for site, domainType := range directList.Domain {
		if domainType == domainTypeDirect {
			lst = append(lst, site)
		}
	}
	return lst
}

var directList = newDirectList()

func initDomainList(domainListFile string, domainType DomainType) {
	var err error
	if err = isFileExists(domainListFile); err != nil {
		return
	}
	f, err := os.Open(domainListFile)
	if err != nil {
		errl.Println("Error opening domain list:", err)
		return
	}
	defer f.Close()

	directList.Lock()
	defer directList.Unlock()
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		domain := strings.TrimSpace(scanner.Text())
		if domain == "" {
			continue
		}
		debug.Printf("Loaded domain %s as type %v", domain, domainType)
		directList.Domain[domain] = domainType
	}
	if scanner.Err() != nil {
		errl.Printf("Error reading domain list %s: %v\n", domainListFile, scanner.Err())
	}
}
<commit_msg>fix reject when no parent server<commit_after>package main

import (
	"net"
	"os"
	"strings"
	"sync"

	"github.com/cyfdecyf/bufio"
)

type DirectList struct {
	Domain map[string]DomainType
	sync.RWMutex
}

type DomainType byte

const (
	domainTypeUnknown DomainType = iota
	domainTypeDirect
	domainTypeProxy
	domainTypeReject
)
const MAX_URL_DOT = 5

func newDirectList() *DirectList {
	return &DirectList{
		Domain: map[string]DomainType{},
	}
}

func charIndex(url string, c byte) []int {
	indexes := make([]int, 0, MAX_URL_DOT+1)
	n := len(url)

	if n <= 1 {
		return indexes
	}

	for i := 0; i < n-1; i++ {
		if url[i] == c {
			indexes = append(indexes, i)
		}
	}
	return indexes
}

func domainSearch(Domain map[string]DomainType, url string, isIP bool) (domainType DomainType, ok bool) {
	if domainType, ok = Domain[url]; !ok && !isIP {
		indexes := charIndex(url, '.')
		n := len(indexes)
		if n > MAX_URL_DOT {
			indexes = indexes[n-MAX_URL_DOT:]
			n = MAX_URL_DOT
		}

		for i := 0; i < n; i++ {
			url_suffix := url[indexes[i]+1:]
			if domainType, ok = Domain[url_suffix]; ok {
				break
			}
		}
	}

	if parentProxy.empty() {
		if domainType == domainTypeReject {
			return domainTypeReject, true
		} else {
			return domainTypeDirect, true
		}
	}
	return
}

func (directList *DirectList) shouldDirect(url *URL) (domainType DomainType) {
	debug.Printf("judging host: %s", url.Host)

	if url.Domain == "" { // simple host or private ip
		return domainTypeDirect
	}

	isIP, isPrivate := hostIsIP(url.Host)
	if v, ok := domainSearch(directList.Domain, url.Host, isIP); ok {
		return v
	}

	if !config.JudgeByIP {
		return domainTypeProxy
	}

	var ip string
	if isIP {
		if isPrivate {
			directList.add(url.Host, domainTypeDirect)
			return domainTypeDirect
		}
		ip = url.Host
	} else {
		hostIPs, err := net.LookupIP(url.Host)
		if err != nil {
			errl.Printf("error looking up host ip %s, err %s", url.Host, err)
			return domainTypeProxy
		}
		ip = hostIPs[0].String()
	}

	if ipShouldDirect(ip) {
		directList.add(url.Host, domainTypeDirect)
		return domainTypeDirect
	} else {
		directList.add(url.Host, domainTypeProxy)
		return domainTypeProxy
	}
}

func (directList *DirectList) add(host string, domainType DomainType) {
	directList.Lock()
	defer directList.Unlock()
	directList.Domain[host] = domainType
}

func (directList *DirectList) GetDirectList() []string {
	lst := make([]string, 0)
	for site, domainType := range directList.Domain {
		if domainType == domainTypeDirect {
			lst = append(lst, site)
		}
	}
	return lst
}

var directList = newDirectList()

func initDomainList(domainListFile string, domainType DomainType) {
	var err error
	if err = isFileExists(domainListFile); err != nil {
		return
	}
	f, err := os.Open(domainListFile)
	if err != nil {
		errl.Println("Error opening domain list:", err)
		return
	}
	defer f.Close()

	directList.Lock()
	defer directList.Unlock()
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		domain := strings.TrimSpace(scanner.Text())
		if domain == "" {
			continue
		}
		debug.Printf("Loaded domain %s as type %v", domain, domainType)
		directList.Domain[domain] = domainType
	}
	if scanner.Err() != nil {
		errl.Printf("Error reading domain list %s: %v\n", domainListFile, scanner.Err())
	}
}
<|endoftext|>


<commit_before>package goandroid

import (
	"github.com/kunaldawn/goandroid/activity"
	"github.com/kunaldawn/goandroid/adbutility"
	"github.com/kunaldawn/goandroid/device"
	"github.com/kunaldawn/goandroid/display"
	"github.com/kunaldawn/goandroid/input"
	"github.com/kunaldawn/goandroid/view"
)

// AndroidManager struct defines a android device manager with an associated
// adb endpoint and adb operation timeout. All devices returned by this android
// manader will be having this adb operation timeout.
type AndroidManager struct {
	Endpoint adbutility.AdbEndpoint // Associated adb endpoint
	Timeout  int                    // Default adb operation timeout in seconds
}

// Android struct defines an android device. Android device has raw device
// communication interface via Device struct, device input interaction interface
// via Input struct, device ui view query interface via View struct, device display
// information interface via Display struct and device application activity
// interface via Activity struct. See their respective documentation for
// list of available mechanisms.
type Android struct {
	Device   device.Device      // Raw adb device communication interface
	Input    input.InputManager // Device input interaction interface
	View     view.DeviceView    // Device UI View query interface
	Display  display.Display    // Device display insterface
	Activity activity.Activity  // Device application activity interface
}

// GetAndroidManager method returns a new AndroidManager instance based on specified
// adb executable path and adb operation timeout. Please note that adb executable must
// be present on the specified path. If adb is on system path then just pass "adb".
// Timeout specified is in seconds and all adb commands will timeout after specified
// seconds.
func GetNewAndroidManager(timeout int, adb string) AndroidManager {
	return AndroidManager{Endpoint: adbutility.GetNewAdbEndpoint(adb), Timeout: timeout}
}

// GetNewAndroidDevice method returns a new Android device instance based on the
// specified serial number.
func (am AndroidManager) GetNewAndroidDevice(serial string) Android {
	dev := device.NewDevice(serial, am.Timeout, am.Endpoint)
	inp := input.NewInputManager(dev)
	viw := view.NewDeviceView(dev)
	disp := display.NewDisplay(dev)
	act := activity.NewActivity(dev)
	return Android{dev, inp, viw, disp, act}
}

// GetAttachedAndroidDevices method returns list of attached android devices
// to the system. It returns error if any error occured wile performing adb operation.
func (am AndroidManager) GetAttachedAndroidDevices() ([]Android, error) {
	serials, err := am.Endpoint.GetAttachedDevices(am.Timeout)
	if err != nil {
		return []Android{}, err
	}
	devices := []Android{}
	for index := range serials {
		dev := am.GetNewAndroidDevice(serials[index])
		devices = append(devices, dev)
	}
	return devices, nil
}
<commit_msg>Typo fixes<commit_after>package goandroid

import (
	"github.com/kunaldawn/goandroid/activity"
	"github.com/kunaldawn/goandroid/adbutility"
	"github.com/kunaldawn/goandroid/device"
	"github.com/kunaldawn/goandroid/display"
	"github.com/kunaldawn/goandroid/input"
	"github.com/kunaldawn/goandroid/view"
)

// AndroidManager struct defines a android device manager with an associated
// adb endpoint and adb operation timeout. All devices returned by this android
// manager will be having this adb operation timeout.
type AndroidManager struct {
	Endpoint adbutility.AdbEndpoint // Associated adb endpoint
	Timeout  int                    // Default adb operation timeout in seconds
}

// Android struct defines an android device. Android device has raw device
// communication interface via Device struct, device input interaction interface
// via Input struct, device ui view query interface via View struct, device display
// information interface via Display struct and device application activity
// interface via Activity struct. See their respective documentation for
// list of available mechanisms.
type Android struct {
	Device   device.Device      // Raw adb device communication interface
	Input    input.InputManager // Device input interaction interface
	View     view.DeviceView    // Device UI View query interface
	Display  display.Display    // Device display insterface
	Activity activity.Activity  // Device application activity interface
}

// GetAndroidManager method returns a new AndroidManager instance based on specified
// adb executable path and adb operation timeout. Please note that adb executable must
// be present on the specified path. If adb is on system path then just pass "adb".
// Timeout specified is in seconds and all adb commands will timeout after specified
// seconds.
func GetNewAndroidManager(timeout int, adb string) AndroidManager {
	return AndroidManager{Endpoint: adbutility.GetNewAdbEndpoint(adb), Timeout: timeout}
}

// GetNewAndroidDevice method returns a new Android device instance based on the
// specified serial number.
func (am AndroidManager) GetNewAndroidDevice(serial string) Android {
	dev := device.NewDevice(serial, am.Timeout, am.Endpoint)
	inp := input.NewInputManager(dev)
	viw := view.NewDeviceView(dev)
	disp := display.NewDisplay(dev)
	act := activity.NewActivity(dev)
	return Android{dev, inp, viw, disp, act}
}

// GetAttachedAndroidDevices method returns list of attached android devices
// to the system. It returns error if any error occurred wile performing adb operation.
func (am AndroidManager) GetAttachedAndroidDevices() ([]Android, error) {
	serials, err := am.Endpoint.GetAttachedDevices(am.Timeout)
	if err != nil {
		return []Android{}, err
	}
	devices := []Android{}
	for index := range serials {
		dev := am.GetNewAndroidDevice(serials[index])
		devices = append(devices, dev)
	}
	return devices, nil
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"path"

	"github.com/mattn/go-gtk/gdk"
	"github.com/mattn/go-gtk/gtk"
)

type UI struct {
	window *gtk.Window

	accelGroup  *gtk.AccelGroup
	actionGroup *gtk.ActionGroup

	vbox     *gtk.VBox
	menubar  *gtk.Widget
	notebook *gtk.Notebook
	tabs     []*Tab

	footer struct {
		table *gtk.Table

		findEntry *gtk.Entry
		replEntry *gtk.Entry

		regBtn  *gtk.ToggleButton
		caseBtn *gtk.ToggleButton

		findNextBtn *gtk.Button
		findPrevBtn *gtk.Button

		replBtn    *gtk.Button
		replAllBtn *gtk.Button

		closeBtn *gtk.Button
	}
}

func CreateUI() *UI {
	ui := &UI{}
	ui.window = gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
	ui.window.SetDefaultSize(600, 300)
	ui.window.SetSizeRequest(100, 100)

	ui.vbox = gtk.NewVBox(false, 0)
	ui.vbox.PackStart(ui.createUIManager(), false, false, 0)

	ui.notebook = gtk.NewNotebook()
	ui.vbox.PackStart(ui.notebook, true, true, 0)
	ui.vbox.PackStart(ui.createFooter(), false, false, 0)
	ui.window.Add(ui.vbox)

	ui.window.Connect("destroy", ui.Quit)
	if conf.Tabs.Homogenous {
		ui.window.Connect("check-resize", ui.homogeneousTabs)
	}

	ui.window.ShowAll()

	ui.footer.table.SetVisible(false)
	ui.menubar.SetVisible(conf.UI.MenuBarVisible)
	return ui
}

func (ui *UI) Open() {
	dialog := gtk.NewFileChooserDialog("Open File", ui.window, gtk.FILE_CHOOSER_ACTION_OPEN, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT)

	if dialog.Run() == gtk.RESPONSE_ACCEPT {
		ui.NewTab(dialog.GetFilename())
	}
	dialog.Destroy()
}
func (ui *UI) Save() {
	t := ui.GetCurrentTab()

	if len(t.Filename) == 0 {
		filename := dialogSave()
		if len(filename) == 0 {
			return
		}

		t.Filename = filename
		t.label.SetText(path.Base(filename))
		t.label.SetTooltipText(filename)
	}
	t.Save()
}
func (ui *UI) SaveAs() {
	t := ui.GetCurrentTab()

	filename := dialogSave()
	if len(filename) == 0 {
		return
	}

	t.Filename = filename
	t.label.SetText(path.Base(filename))
	t.label.SetTooltipText(filename)
	t.Save()
}

// func (ui *UI) CloseCurrentTab() {
// 	ui.CloseCurrentTab()

// 	if len(ui.tabs) == 0 {
// 		gtk.MainQuit()
// 	}
// }

func (ui *UI) Quit() {
	for _, t := range ui.tabs {
		t.File.Close()
	}
	gtk.MainQuit()
}

func (ui *UI) Find() {
	ui.GetCurrentTab().Find()
}
func (ui *UI) FindNext() {
	ui.GetCurrentTab().FindNext(true)
}
func (ui *UI) FindPrev() {
	ui.GetCurrentTab().FindNext(false)
}
func (ui *UI) ReplaceOne() {
	ui.GetCurrentTab().Replace(false)
}
func (ui *UI) ReplaceAll() {
	ui.GetCurrentTab().Replace(true)
}

func (ui *UI) ToggleMenuBar() {
	conf.UI.MenuBarVisible = !conf.UI.MenuBarVisible
	ui.menubar.SetVisible(conf.UI.MenuBarVisible)

}
func (ui *UI) ToggleStatusBar() {
	log.Println("statusbar")
	// conf.UI.StatusBarVisible = !conf.UI.StatusBarVisible
	// ui.statusbar.SetVisible(conf.UI.StatusBarVisible)
	// ui.menu.statusbar.SetActive(conf.UI.StatusBarVisible)
}

func dialogSave() string {
	dialog := gtk.NewFileChooserDialog("Save File", ui.window, gtk.FILE_CHOOSER_ACTION_SAVE, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT)

	var filename string
	if dialog.Run() == gtk.RESPONSE_ACCEPT {
		filename = dialog.GetFilename()
	}

	dialog.Destroy()

	return filename
}

func (ui *UI) createUIManager() *gtk.Widget {
	UIxml := `
<ui>
	<menubar name='MenuBar'>

		<menu action='File'>
			<menuitem action='NewTab' />
			<menuitem action='Open' />
			<menuitem action='Save' />
			<menuitem action='SaveAs' />
			<separator />
			<menuitem action='CloseTab' />
			<menuitem action='Quit' />
		</menu>

		<menu action='Edit'>
			<menuitem action='Find'/>
			<menuitem action='FindNext'/>
			<menuitem action='FindPrev'/>
			<separator />
			<menuitem action='Replace'/>
			<menuitem action='ReplaceOne'/>
			<menuitem action='ReplaceAll'/>
		</menu>

		<menu name='View' action='View'>
			<menuitem action='Menubar'/>
		</menu>

	</menubar>
</ui>
`
	uiManager := gtk.NewUIManager()
	uiManager.AddUIFromString(UIxml)

	ui.accelGroup = uiManager.GetAccelGroup()
	ui.window.AddAccelGroup(ui.accelGroup)

	ui.actionGroup = gtk.NewActionGroup("my_group")
	uiManager.InsertActionGroup(ui.actionGroup, 0)

	// File
	ui.actionGroup.AddAction(gtk.NewAction("File", "File", "", ""))

	ui.newAction("NewTab", "New Tab", "<control>t", func() { ui.NewTab("") })
	ui.newActionStock("Open", gtk.STOCK_OPEN, "", ui.Open)
	ui.newActionStock("Save", gtk.STOCK_SAVE, "", ui.Save)
	ui.newActionStock("SaveAs", gtk.STOCK_SAVE_AS, "", ui.SaveAs)
	ui.newAction("CloseTab", "Close Tab", "<control>w", ui.CloseCurrentTab)
	ui.newActionStock("Quit", gtk.STOCK_QUIT, "", ui.Quit)

	// Edit
	ui.actionGroup.AddAction(gtk.NewAction("Edit", "Edit", "", ""))

	ui.newActionStock("Find", gtk.STOCK_FIND, "", ui.ShowFindbar)
	ui.newAction("FindNext", "Find Next", "F3", ui.FindNext)
	ui.newAction("FindPrev", "Find Previous", "<shift>F3", ui.FindPrev)

	ui.newActionStock("Replace", gtk.STOCK_FIND_AND_REPLACE, "<control>h", ui.ShowReplbar)
	ui.newAction("ReplaceOne", "Replace One", "<control><shift>h", ui.ReplaceOne)
	ui.newAction("ReplaceAll", "Replace All", "<control><alt>Return", ui.ReplaceAll)

	// View
	ui.actionGroup.AddAction(gtk.NewAction("View", "View", "", ""))
	// ui.actionGroup.AddAction(gtk.NewAction("Encoding", "Encoding", "", ""))

	ui.newToggleAction("Menubar", "Menubar", "<control>M", conf.UI.MenuBarVisible, ui.ToggleMenuBar)

	ui.menubar = uiManager.GetWidget("/MenuBar")

	return ui.menubar
}

func (ui *UI) newAction(dst, label, accel string, f func()) {
	action := gtk.NewAction(dst, label, "", "")
	action.Connect("activate", f)
	ui.actionGroup.AddActionWithAccel(action, accel)
}

func (ui *UI) newActionStock(dst, stock, accel string, f func()) {
	action := gtk.NewAction(dst, "", "", stock)
	action.Connect("activate", f)
	ui.actionGroup.AddActionWithAccel(action, accel)
}

func (ui *UI) newToggleAction(dst, label, accel string, state bool, f func()) {
	action := gtk.NewToggleAction(dst, label, "", "")
	action.SetActive(state)
	action.Connect("activate", f)
	ui.actionGroup.AddActionWithAccel(&action.Action, accel)
}

func (ui *UI) homogeneousTabs() {
	if len(ui.tabs) == 0 || !conf.Tabs.Homogenous {
		return
	}

	var width, height int
	ui.window.GetSize(&width, &height)

	tabwidth := (width - len(ui.tabs)*6) / len(ui.tabs)
	leftwidth := (width - len(ui.tabs)*6) % len(ui.tabs)

	for _, t := range ui.tabs {
		if leftwidth > 0 {
			t.tabbox.SetSizeRequest(tabwidth+1, conf.Tabs.Height)
			leftwidth--
		} else {
			t.tabbox.SetSizeRequest(tabwidth, conf.Tabs.Height)
		}
	}
}

func (ui *UI) TabsContains(filename string) bool {
	for n, t := range ui.tabs {
		if t.Filename == filename {
			ui.notebook.SetCurrentPage(n)
			return true
		}
	}
	return false
}

func (ui *UI) CloseCurrentTab() {
	n := ui.notebook.GetCurrentPage()
	ui.CloseTab(n)
}

func (ui *UI) CloseTab(n int) {
	ui.tabs[n].Close()

	if len(ui.tabs) == 0 {
		gtk.MainQuit()
	}
}

func (ui *UI) GetCurrentTab() *Tab {
	n := ui.notebook.GetCurrentPage()
	if n < 0 {
		return nil
	}
	return ui.tabs[n]
}

func (ui *UI) createFooter() *gtk.Table {
	ui.footer.table = gtk.NewTable(2, 6, false)

	// findbar
	labelReg := gtk.NewLabel("Re")
	labelReg.ModifyFG(gtk.STATE_ACTIVE, gdk.NewColor("red"))
	ui.footer.regBtn = gtk.NewToggleButton()
	ui.footer.regBtn.Add(labelReg)
	ui.footer.regBtn.Connect("toggled", ui.Find)

	labelCase := gtk.NewLabel("A")
	labelCase.ModifyFG(gtk.STATE_ACTIVE, gdk.NewColor("red"))
	ui.footer.caseBtn = gtk.NewToggleButton()
	ui.footer.caseBtn.Add(labelCase)
	ui.footer.caseBtn.SetSizeRequest(20, 20)
	ui.footer.caseBtn.Connect("toggled", ui.Find)

	ui.footer.findEntry = gtk.NewEntryWithBuffer(gtk.NewEntryBuffer(""))
	ui.footer.findEntry.Connect("changed", ui.Find)

	ui.footer.findNextBtn = gtk.NewButton()
	ui.footer.findNextBtn.SetSizeRequest(20, 20)
	ui.footer.findNextBtn.Add(gtk.NewArrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE))
	ui.footer.findNextBtn.Clicked(ui.FindNext)

	ui.footer.findPrevBtn = gtk.NewButton()
	ui.footer.findPrevBtn.SetSizeRequest(20, 20)
	ui.footer.findPrevBtn.Add(gtk.NewArrow(gtk.ARROW_UP, gtk.SHADOW_NONE))
	ui.footer.findPrevBtn.Clicked(ui.FindPrev)

	ui.footer.closeBtn = gtk.NewButton()
	ui.footer.closeBtn.SetSizeRequest(20, 20)
	ui.footer.closeBtn.Add(gtk.NewImageFromStock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_BUTTON))
	ui.footer.closeBtn.Clicked(ui.FooterClose)
	ui.footer.closeBtn.AddAccelerator("activate", ui.accelGroup, gdk.KEY_Escape, 0, gtk.ACCEL_VISIBLE)

	// replacebar
	ui.footer.replEntry = gtk.NewEntryWithBuffer(gtk.NewEntryBuffer(""))
	// ui.footer.replEntry.Connect("changed", OnFindInput)

	ui.footer.replBtn = gtk.NewButton()
	ui.footer.replBtn.SetSizeRequest(20, 20)
	ui.footer.replBtn.Add(gtk.NewImageFromIconName("text-changelog", gtk.ICON_SIZE_BUTTON))
	ui.footer.replBtn.Clicked(ui.ReplaceOne)

	ui.footer.replAllBtn = gtk.NewButton()
	ui.footer.replAllBtn.SetSizeRequest(20, 20)
	ui.footer.replAllBtn.Add(gtk.NewImageFromIconName("text-plain", gtk.ICON_SIZE_BUTTON))
	ui.footer.replAllBtn.Clicked(ui.ReplaceAll)
	// btnRepl.Clicked(OnMenuFind)

	// pack to table
	ui.footer.table.Attach(ui.footer.regBtn, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.caseBtn, 1, 2, 0, 1, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.findEntry, 2, 3, 0, 1, gtk.EXPAND|gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.findNextBtn, 3, 4, 0, 1, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.findPrevBtn, 4, 5, 0, 1, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.closeBtn, 5, 6, 0, 1, gtk.FILL, gtk.FILL, 0, 0)

	ui.footer.table.Attach(ui.footer.replEntry, 2, 3, 1, 2, gtk.EXPAND|gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.replBtn, 3, 4, 1, 2, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.replAllBtn, 4, 5, 1, 2, gtk.FILL, gtk.FILL, 0, 0)

	return ui.footer.table
}

func (ui *UI) ShowFindbar() {
	ui.footer.table.SetVisible(true)
	ui.footer.replEntry.SetVisible(false)
	ui.footer.replBtn.SetVisible(false)
	ui.footer.replAllBtn.SetVisible(false)

	ui.footer.findEntry.GrabFocus()
}

func (ui *UI) ShowReplbar() {
	ui.footer.table.SetVisible(true)
	ui.footer.replEntry.SetVisible(true)
	ui.footer.replBtn.SetVisible(true)
	ui.footer.replAllBtn.SetVisible(true)

	ui.footer.replEntry.GrabFocus()
}

func (ui *UI) FooterClose() {
	ui.footer.table.SetVisible(false)
}
<commit_msg>add saveas hotkey<commit_after>package main

import (
	"log"
	"path"

	"github.com/mattn/go-gtk/gdk"
	"github.com/mattn/go-gtk/gtk"
)

type UI struct {
	window *gtk.Window

	accelGroup  *gtk.AccelGroup
	actionGroup *gtk.ActionGroup

	vbox     *gtk.VBox
	menubar  *gtk.Widget
	notebook *gtk.Notebook
	tabs     []*Tab

	footer struct {
		table *gtk.Table

		findEntry *gtk.Entry
		replEntry *gtk.Entry

		regBtn  *gtk.ToggleButton
		caseBtn *gtk.ToggleButton

		findNextBtn *gtk.Button
		findPrevBtn *gtk.Button

		replBtn    *gtk.Button
		replAllBtn *gtk.Button

		closeBtn *gtk.Button
	}
}

func CreateUI() *UI {
	ui := &UI{}
	ui.window = gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
	ui.window.SetDefaultSize(600, 300)
	ui.window.SetSizeRequest(100, 100)

	ui.vbox = gtk.NewVBox(false, 0)
	ui.vbox.PackStart(ui.createUIManager(), false, false, 0)

	ui.notebook = gtk.NewNotebook()
	ui.vbox.PackStart(ui.notebook, true, true, 0)
	ui.vbox.PackStart(ui.createFooter(), false, false, 0)
	ui.window.Add(ui.vbox)

	ui.window.Connect("destroy", ui.Quit)
	if conf.Tabs.Homogenous {
		ui.window.Connect("check-resize", ui.homogeneousTabs)
	}

	ui.window.ShowAll()

	ui.footer.table.SetVisible(false)
	ui.menubar.SetVisible(conf.UI.MenuBarVisible)
	return ui
}

func (ui *UI) Open() {
	dialog := gtk.NewFileChooserDialog("Open File", ui.window, gtk.FILE_CHOOSER_ACTION_OPEN, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT)

	if dialog.Run() == gtk.RESPONSE_ACCEPT {
		ui.NewTab(dialog.GetFilename())
	}
	dialog.Destroy()
}
func (ui *UI) Save() {
	t := ui.GetCurrentTab()

	if len(t.Filename) == 0 {
		filename := dialogSave()
		if len(filename) == 0 {
			return
		}

		t.Filename = filename
		t.label.SetText(path.Base(filename))
		t.label.SetTooltipText(filename)
	}
	t.Save()
}
func (ui *UI) SaveAs() {
	t := ui.GetCurrentTab()

	filename := dialogSave()
	if len(filename) == 0 {
		return
	}

	t.Filename = filename
	t.label.SetText(path.Base(filename))
	t.label.SetTooltipText(filename)
	t.Save()
}

// func (ui *UI) CloseCurrentTab() {
// 	ui.CloseCurrentTab()

// 	if len(ui.tabs) == 0 {
// 		gtk.MainQuit()
// 	}
// }

func (ui *UI) Quit() {
	for _, t := range ui.tabs {
		t.File.Close()
	}
	gtk.MainQuit()
}

func (ui *UI) Find() {
	ui.GetCurrentTab().Find()
}
func (ui *UI) FindNext() {
	ui.GetCurrentTab().FindNext(true)
}
func (ui *UI) FindPrev() {
	ui.GetCurrentTab().FindNext(false)
}
func (ui *UI) ReplaceOne() {
	ui.GetCurrentTab().Replace(false)
}
func (ui *UI) ReplaceAll() {
	ui.GetCurrentTab().Replace(true)
}

func (ui *UI) ToggleMenuBar() {
	conf.UI.MenuBarVisible = !conf.UI.MenuBarVisible
	ui.menubar.SetVisible(conf.UI.MenuBarVisible)

}
func (ui *UI) ToggleStatusBar() {
	log.Println("statusbar")
	// conf.UI.StatusBarVisible = !conf.UI.StatusBarVisible
	// ui.statusbar.SetVisible(conf.UI.StatusBarVisible)
	// ui.menu.statusbar.SetActive(conf.UI.StatusBarVisible)
}

func dialogSave() string {
	dialog := gtk.NewFileChooserDialog("Save File", ui.window, gtk.FILE_CHOOSER_ACTION_SAVE, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT)

	var filename string
	if dialog.Run() == gtk.RESPONSE_ACCEPT {
		filename = dialog.GetFilename()
	}

	dialog.Destroy()

	return filename
}

func (ui *UI) createUIManager() *gtk.Widget {
	UIxml := `
<ui>
	<menubar name='MenuBar'>

		<menu action='File'>
			<menuitem action='NewTab' />
			<menuitem action='Open' />
			<menuitem action='Save' />
			<menuitem action='SaveAs' />
			<separator />
			<menuitem action='CloseTab' />
			<menuitem action='Quit' />
		</menu>

		<menu action='Edit'>
			<menuitem action='Find'/>
			<menuitem action='FindNext'/>
			<menuitem action='FindPrev'/>
			<separator />
			<menuitem action='Replace'/>
			<menuitem action='ReplaceOne'/>
			<menuitem action='ReplaceAll'/>
		</menu>

		<menu name='View' action='View'>
			<menuitem action='Menubar'/>
		</menu>

	</menubar>
</ui>
`
	uiManager := gtk.NewUIManager()
	uiManager.AddUIFromString(UIxml)

	ui.accelGroup = uiManager.GetAccelGroup()
	ui.window.AddAccelGroup(ui.accelGroup)

	ui.actionGroup = gtk.NewActionGroup("my_group")
	uiManager.InsertActionGroup(ui.actionGroup, 0)

	// File
	ui.actionGroup.AddAction(gtk.NewAction("File", "File", "", ""))

	ui.newAction("NewTab", "New Tab", "<control>t", func() { ui.NewTab("") })
	ui.newActionStock("Open", gtk.STOCK_OPEN, "", ui.Open)
	ui.newActionStock("Save", gtk.STOCK_SAVE, "", ui.Save)
	ui.newActionStock("SaveAs", gtk.STOCK_SAVE_AS, "<control><shift>s", ui.SaveAs)
	ui.newAction("CloseTab", "Close Tab", "<control>w", ui.CloseCurrentTab)
	ui.newActionStock("Quit", gtk.STOCK_QUIT, "", ui.Quit)

	// Edit
	ui.actionGroup.AddAction(gtk.NewAction("Edit", "Edit", "", ""))

	ui.newActionStock("Find", gtk.STOCK_FIND, "", ui.ShowFindbar)
	ui.newAction("FindNext", "Find Next", "F3", ui.FindNext)
	ui.newAction("FindPrev", "Find Previous", "<shift>F3", ui.FindPrev)

	ui.newActionStock("Replace", gtk.STOCK_FIND_AND_REPLACE, "<control>h", ui.ShowReplbar)
	ui.newAction("ReplaceOne", "Replace One", "<control><shift>h", ui.ReplaceOne)
	ui.newAction("ReplaceAll", "Replace All", "<control><alt>Return", ui.ReplaceAll)

	// View
	ui.actionGroup.AddAction(gtk.NewAction("View", "View", "", ""))
	// ui.actionGroup.AddAction(gtk.NewAction("Encoding", "Encoding", "", ""))

	ui.newToggleAction("Menubar", "Menubar", "<control>M", conf.UI.MenuBarVisible, ui.ToggleMenuBar)

	ui.menubar = uiManager.GetWidget("/MenuBar")

	return ui.menubar
}

func (ui *UI) newAction(dst, label, accel string, f func()) {
	action := gtk.NewAction(dst, label, "", "")
	action.Connect("activate", f)
	ui.actionGroup.AddActionWithAccel(action, accel)
}

func (ui *UI) newActionStock(dst, stock, accel string, f func()) {
	action := gtk.NewAction(dst, "", "", stock)
	action.Connect("activate", f)
	ui.actionGroup.AddActionWithAccel(action, accel)
}

func (ui *UI) newToggleAction(dst, label, accel string, state bool, f func()) {
	action := gtk.NewToggleAction(dst, label, "", "")
	action.SetActive(state)
	action.Connect("activate", f)
	ui.actionGroup.AddActionWithAccel(&action.Action, accel)
}

func (ui *UI) homogeneousTabs() {
	if len(ui.tabs) == 0 || !conf.Tabs.Homogenous {
		return
	}

	var width, height int
	ui.window.GetSize(&width, &height)

	tabwidth := (width - len(ui.tabs)*6) / len(ui.tabs)
	leftwidth := (width - len(ui.tabs)*6) % len(ui.tabs)

	for _, t := range ui.tabs {
		if leftwidth > 0 {
			t.tabbox.SetSizeRequest(tabwidth+1, conf.Tabs.Height)
			leftwidth--
		} else {
			t.tabbox.SetSizeRequest(tabwidth, conf.Tabs.Height)
		}
	}
}

func (ui *UI) TabsContains(filename string) bool {
	for n, t := range ui.tabs {
		if t.Filename == filename {
			ui.notebook.SetCurrentPage(n)
			return true
		}
	}
	return false
}

func (ui *UI) CloseCurrentTab() {
	n := ui.notebook.GetCurrentPage()
	ui.CloseTab(n)
}

func (ui *UI) CloseTab(n int) {
	ui.tabs[n].Close()

	if len(ui.tabs) == 0 {
		gtk.MainQuit()
	}
}

func (ui *UI) GetCurrentTab() *Tab {
	n := ui.notebook.GetCurrentPage()
	if n < 0 {
		return nil
	}
	return ui.tabs[n]
}

func (ui *UI) createFooter() *gtk.Table {
	ui.footer.table = gtk.NewTable(2, 6, false)

	// findbar
	labelReg := gtk.NewLabel("Re")
	labelReg.ModifyFG(gtk.STATE_ACTIVE, gdk.NewColor("red"))
	ui.footer.regBtn = gtk.NewToggleButton()
	ui.footer.regBtn.Add(labelReg)
	ui.footer.regBtn.Connect("toggled", ui.Find)

	labelCase := gtk.NewLabel("A")
	labelCase.ModifyFG(gtk.STATE_ACTIVE, gdk.NewColor("red"))
	ui.footer.caseBtn = gtk.NewToggleButton()
	ui.footer.caseBtn.Add(labelCase)
	ui.footer.caseBtn.SetSizeRequest(20, 20)
	ui.footer.caseBtn.Connect("toggled", ui.Find)

	ui.footer.findEntry = gtk.NewEntryWithBuffer(gtk.NewEntryBuffer(""))
	ui.footer.findEntry.Connect("changed", ui.Find)

	ui.footer.findNextBtn = gtk.NewButton()
	ui.footer.findNextBtn.SetSizeRequest(20, 20)
	ui.footer.findNextBtn.Add(gtk.NewArrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE))
	ui.footer.findNextBtn.Clicked(ui.FindNext)

	ui.footer.findPrevBtn = gtk.NewButton()
	ui.footer.findPrevBtn.SetSizeRequest(20, 20)
	ui.footer.findPrevBtn.Add(gtk.NewArrow(gtk.ARROW_UP, gtk.SHADOW_NONE))
	ui.footer.findPrevBtn.Clicked(ui.FindPrev)

	ui.footer.closeBtn = gtk.NewButton()
	ui.footer.closeBtn.SetSizeRequest(20, 20)
	ui.footer.closeBtn.Add(gtk.NewImageFromStock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_BUTTON))
	ui.footer.closeBtn.Clicked(ui.FooterClose)
	ui.footer.closeBtn.AddAccelerator("activate", ui.accelGroup, gdk.KEY_Escape, 0, gtk.ACCEL_VISIBLE)

	// replacebar
	ui.footer.replEntry = gtk.NewEntryWithBuffer(gtk.NewEntryBuffer(""))
	// ui.footer.replEntry.Connect("changed", OnFindInput)

	ui.footer.replBtn = gtk.NewButton()
	ui.footer.replBtn.SetSizeRequest(20, 20)
	ui.footer.replBtn.Add(gtk.NewImageFromIconName("text-changelog", gtk.ICON_SIZE_BUTTON))
	ui.footer.replBtn.Clicked(ui.ReplaceOne)

	ui.footer.replAllBtn = gtk.NewButton()
	ui.footer.replAllBtn.SetSizeRequest(20, 20)
	ui.footer.replAllBtn.Add(gtk.NewImageFromIconName("text-plain", gtk.ICON_SIZE_BUTTON))
	ui.footer.replAllBtn.Clicked(ui.ReplaceAll)
	// btnRepl.Clicked(OnMenuFind)

	// pack to table
	ui.footer.table.Attach(ui.footer.regBtn, 0, 1, 0, 1, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.caseBtn, 1, 2, 0, 1, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.findEntry, 2, 3, 0, 1, gtk.EXPAND|gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.findNextBtn, 3, 4, 0, 1, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.findPrevBtn, 4, 5, 0, 1, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.closeBtn, 5, 6, 0, 1, gtk.FILL, gtk.FILL, 0, 0)

	ui.footer.table.Attach(ui.footer.replEntry, 2, 3, 1, 2, gtk.EXPAND|gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.replBtn, 3, 4, 1, 2, gtk.FILL, gtk.FILL, 0, 0)
	ui.footer.table.Attach(ui.footer.replAllBtn, 4, 5, 1, 2, gtk.FILL, gtk.FILL, 0, 0)

	return ui.footer.table
}

func (ui *UI) ShowFindbar() {
	ui.footer.table.SetVisible(true)
	ui.footer.replEntry.SetVisible(false)
	ui.footer.replBtn.SetVisible(false)
	ui.footer.replAllBtn.SetVisible(false)

	ui.footer.findEntry.GrabFocus()
}

func (ui *UI) ShowReplbar() {
	ui.footer.table.SetVisible(true)
	ui.footer.replEntry.SetVisible(true)
	ui.footer.replBtn.SetVisible(true)
	ui.footer.replAllBtn.SetVisible(true)

	ui.footer.replEntry.GrabFocus()
}

func (ui *UI) FooterClose() {
	ui.footer.table.SetVisible(false)
}
<|endoftext|>


<commit_before>package goshopify

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/google/go-querystring/query"
)

const (
	UserAgent = "goshopify/0.2.0"
)

// Basic app settings such as Api key, secret, scope, and redirect url.
// See oauth.go for OAuth related helper functions.
type App struct {
	ApiKey      string
	ApiSecret   string
	RedirectUrl string
	Scope       string
}

// Client manages communication with the Shopify API.
type Client struct {
	// HTTP client used to communicate with the DO API.
	client *http.Client

	// App settings
	app App

	// Base URL for API requests.
	// This is set on a per-store basis which means that each store must have
	// its own client.
	baseURL *url.URL

	// A permanent access token
	token string

	// Services used for communicating with the API
	Product  ProductService
	Customer CustomerService
	Order    OrderService
	Shop     ShopService
	Webhook  WebhookService
}

// A general response error that follows a similar layout to Shopify's response
// errors, i.e. either a single message or a list of messages.
type ResponseError struct {
	Status  int
	Message string
	Errors  []string
}

func (e ResponseError) Error() string {
	if e.Message != "" {
		return e.Message
	}

	sort.Strings(e.Errors)
	s := strings.Join(e.Errors, ", ")

	if s != "" {
		return s
	}

	return "Unknown Error"
}

// An error specific to a rate-limiting response. Embeds the ResponseError to
// allow consumers to handle it the same was a normal ResponseError.
type RateLimitError struct {
	ResponseError
	RetryAfter int
}

// Creates an API request. A relative URL can be provided in urlStr, which will
// be resolved to the BaseURL of the Client. Relative URLS should always be
// specified without a preceding slash. If specified, the value pointed to by
// body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body, options interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	// Make the full url based on the relative path
	u := c.baseURL.ResolveReference(rel)

	// Add custom options
	if options != nil {
		optionsQuery, err := query.Values(options)
		if err != nil {
			return nil, err
		}

		for k, values := range u.Query() {
			for _, v := range values {
				optionsQuery.Add(k, v)
			}
		}
		u.RawQuery = optionsQuery.Encode()
	}

	// A bit of JSON ceremony
	var js []byte = nil

	if body != nil {
		js, err = json.Marshal(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), bytes.NewBuffer(js))
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")
	req.Header.Add("User-Agent", UserAgent)
	if c.token != "" {
		req.Header.Add("X-Shopify-Access-Token", c.token)
	}
	return req, nil
}

// Returns a new Shopify API client with an already authenticated shopname and
// token.
func NewClient(app App, shopName, token string) *Client {
	httpClient := http.DefaultClient

	baseURL, _ := url.Parse(ShopBaseUrl(shopName))

	c := &Client{client: httpClient, app: app, baseURL: baseURL, token: token}
	c.Product = &ProductServiceOp{client: c}
	c.Customer = &CustomerServiceOp{client: c}
	c.Order = &OrderServiceOp{client: c}
	c.Shop = &ShopServiceOp{client: c}
	c.Webhook = &WebhookServiceOp{client: c}

	return c
}

// Do sends an API request and populates the given interface with the parsed
// response. It does not make much sense to call Do without a prepared
// interface instance.
func (c *Client) Do(req *http.Request, v interface{}) error {
	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	err = CheckResponseError(resp)
	if err != nil {
		return err
	}

	if v != nil {
		decoder := json.NewDecoder(resp.Body)
		err := decoder.Decode(&v)
		if err != nil {
			return err
		}
	}

	return nil
}

func wrapSpecificError(r *http.Response, err ResponseError) error {
	if err.Status == 429 {
		f, _ := strconv.ParseFloat(r.Header.Get("retry-after"), 64)
		return RateLimitError{
			ResponseError: err,
			RetryAfter:    int(f),
		}
	}
	return err
}

func CheckResponseError(r *http.Response) error {
	if r.StatusCode >= 200 && r.StatusCode < 300 {
		return nil
	}

	// Create an anonoymous struct to parse the JSON data into.
	shopifyError := struct {
		Error  string      `json:"error"`
		Errors interface{} `json:"errors"`
	}{}

	decoder := json.NewDecoder(r.Body)
	err := decoder.Decode(&shopifyError)
	if err != nil {
		return err
	}

	// Create the response error from the Shopify error.
	responseError := ResponseError{
		Status:  r.StatusCode,
		Message: shopifyError.Error,
	}

	// If the errors field is not filled out, we can return here.
	if shopifyError.Errors == nil {
		return wrapSpecificError(r, responseError)
	}

	// Shopify errors usually have the form:
	// {
	//   "errors": {
	//     "title": [
	//       "something is wrong"
	//     ]
	//   }
	// }
	// This structure is flattened to a single array:
	// [ "title: something is wrong" ]
	//
	// Unfortunately, "errors" can also be a single string so we have to deal
	// with that. Lots of reflection :-(
	kind := reflect.TypeOf(shopifyError.Errors).Kind()
	if kind == reflect.String {
		// Single string, use as message
		responseError.Message = shopifyError.Errors.(string)
	} else if kind == reflect.Slice {
		// An array, parse each entry as a string and join them on the message
		// json always serializes JSON arrays into []interface{}
		for _, elem := range shopifyError.Errors.([]interface{}) {
			responseError.Errors = append(responseError.Errors, fmt.Sprint(elem))
		}
		responseError.Message = strings.Join(responseError.Errors, ", ")
	} else if kind == reflect.Map {
		// A map, parse each error for each key in the map.
		// json always serializes into map[string]interface{} for objects
		for k, v := range shopifyError.Errors.(map[string]interface{}) {
			// Check to make sure the interface is a slice
			// json always serializes JSON arrays into []interface{}
			if reflect.TypeOf(v).Kind() == reflect.Slice {
				for _, elem := range v.([]interface{}) {
					// If the primary message of the response error is not set, use
					// any message.
					if responseError.Message == "" {
						responseError.Message = fmt.Sprint(elem)
					}
					topicAndElem := fmt.Sprintf("%v: %v", k, elem)
					responseError.Errors = append(responseError.Errors, topicAndElem)
				}
			}
		}
	}

	return wrapSpecificError(r, responseError)
}

// General list options that can be used for most collections of entities.
type ListOptions struct {
	Page         int       `url:"page,omitempty"`
	Limit        int       `url:"limit,omitempty"`
	SinceID      int       `url:"since_id,omitempty"`
	CreatedAtMin time.Time `url:"created_at_min,omitempty"`
	CreatedAtMax time.Time `url:"created_at_max,omitempty"`
}

// General count options that can be used for most collection counts.
type CountOptions struct {
	CreatedAtMin time.Time `url:"created_at_min,omitempty"`
	CreatedAtMax time.Time `url:"created_at_max,omitempty"`
}

func (c *Client) Count(path string, options interface{}) (int, error) {
	resource := struct {
		Count int `json:"count"`
	}{}
	err := c.Get(path, &resource, options)
	return resource.Count, err
}

func (c *Client) CreateAndDo(method, path string, data, options, resource interface{}) error {
	req, err := c.NewRequest(method, path, data, options)
	if err != nil {
		return err
	}

	err = c.Do(req, resource)
	if err != nil {
		return err
	}

	return nil
}

// Perform a Get request for the given path and save the result in the given
// resource.
func (c *Client) Get(path string, resource, options interface{}) error {
	return c.CreateAndDo("GET", path, nil, options, resource)
}

// Perform a POST request for the given path and save the result in the given
// resource.
func (c *Client) Post(path string, data, resource interface{}) error {
	return c.CreateAndDo("POST", path, data, nil, resource)
}

// Perform a PUT request for the given path and save the result in the given
// resource.
func (c *Client) Put(path string, data, resource interface{}) error {
	return c.CreateAndDo("PUT", path, data, nil, resource)
}
<commit_msg>Additional List and Count Options (#28)<commit_after>package goshopify

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/google/go-querystring/query"
)

const (
	UserAgent = "goshopify/0.2.0"
)

// Basic app settings such as Api key, secret, scope, and redirect url.
// See oauth.go for OAuth related helper functions.
type App struct {
	ApiKey      string
	ApiSecret   string
	RedirectUrl string
	Scope       string
}

// Client manages communication with the Shopify API.
type Client struct {
	// HTTP client used to communicate with the DO API.
	client *http.Client

	// App settings
	app App

	// Base URL for API requests.
	// This is set on a per-store basis which means that each store must have
	// its own client.
	baseURL *url.URL

	// A permanent access token
	token string

	// Services used for communicating with the API
	Product  ProductService
	Customer CustomerService
	Order    OrderService
	Shop     ShopService
	Webhook  WebhookService
}

// A general response error that follows a similar layout to Shopify's response
// errors, i.e. either a single message or a list of messages.
type ResponseError struct {
	Status  int
	Message string
	Errors  []string
}

func (e ResponseError) Error() string {
	if e.Message != "" {
		return e.Message
	}

	sort.Strings(e.Errors)
	s := strings.Join(e.Errors, ", ")

	if s != "" {
		return s
	}

	return "Unknown Error"
}

// An error specific to a rate-limiting response. Embeds the ResponseError to
// allow consumers to handle it the same was a normal ResponseError.
type RateLimitError struct {
	ResponseError
	RetryAfter int
}

// Creates an API request. A relative URL can be provided in urlStr, which will
// be resolved to the BaseURL of the Client. Relative URLS should always be
// specified without a preceding slash. If specified, the value pointed to by
// body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body, options interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	// Make the full url based on the relative path
	u := c.baseURL.ResolveReference(rel)

	// Add custom options
	if options != nil {
		optionsQuery, err := query.Values(options)
		if err != nil {
			return nil, err
		}

		for k, values := range u.Query() {
			for _, v := range values {
				optionsQuery.Add(k, v)
			}
		}
		u.RawQuery = optionsQuery.Encode()
	}

	// A bit of JSON ceremony
	var js []byte = nil

	if body != nil {
		js, err = json.Marshal(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), bytes.NewBuffer(js))
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Accept", "application/json")
	req.Header.Add("User-Agent", UserAgent)
	if c.token != "" {
		req.Header.Add("X-Shopify-Access-Token", c.token)
	}
	return req, nil
}

// Returns a new Shopify API client with an already authenticated shopname and
// token.
func NewClient(app App, shopName, token string) *Client {
	httpClient := http.DefaultClient

	baseURL, _ := url.Parse(ShopBaseUrl(shopName))

	c := &Client{client: httpClient, app: app, baseURL: baseURL, token: token}
	c.Product = &ProductServiceOp{client: c}
	c.Customer = &CustomerServiceOp{client: c}
	c.Order = &OrderServiceOp{client: c}
	c.Shop = &ShopServiceOp{client: c}
	c.Webhook = &WebhookServiceOp{client: c}

	return c
}

// Do sends an API request and populates the given interface with the parsed
// response. It does not make much sense to call Do without a prepared
// interface instance.
func (c *Client) Do(req *http.Request, v interface{}) error {
	resp, err := c.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	err = CheckResponseError(resp)
	if err != nil {
		return err
	}

	if v != nil {
		decoder := json.NewDecoder(resp.Body)
		err := decoder.Decode(&v)
		if err != nil {
			return err
		}
	}

	return nil
}

func wrapSpecificError(r *http.Response, err ResponseError) error {
	if err.Status == 429 {
		f, _ := strconv.ParseFloat(r.Header.Get("retry-after"), 64)
		return RateLimitError{
			ResponseError: err,
			RetryAfter:    int(f),
		}
	}
	return err
}

func CheckResponseError(r *http.Response) error {
	if r.StatusCode >= 200 && r.StatusCode < 300 {
		return nil
	}

	// Create an anonoymous struct to parse the JSON data into.
	shopifyError := struct {
		Error  string      `json:"error"`
		Errors interface{} `json:"errors"`
	}{}

	decoder := json.NewDecoder(r.Body)
	err := decoder.Decode(&shopifyError)
	if err != nil {
		return err
	}

	// Create the response error from the Shopify error.
	responseError := ResponseError{
		Status:  r.StatusCode,
		Message: shopifyError.Error,
	}

	// If the errors field is not filled out, we can return here.
	if shopifyError.Errors == nil {
		return wrapSpecificError(r, responseError)
	}

	// Shopify errors usually have the form:
	// {
	//   "errors": {
	//     "title": [
	//       "something is wrong"
	//     ]
	//   }
	// }
	// This structure is flattened to a single array:
	// [ "title: something is wrong" ]
	//
	// Unfortunately, "errors" can also be a single string so we have to deal
	// with that. Lots of reflection :-(
	kind := reflect.TypeOf(shopifyError.Errors).Kind()
	if kind == reflect.String {
		// Single string, use as message
		responseError.Message = shopifyError.Errors.(string)
	} else if kind == reflect.Slice {
		// An array, parse each entry as a string and join them on the message
		// json always serializes JSON arrays into []interface{}
		for _, elem := range shopifyError.Errors.([]interface{}) {
			responseError.Errors = append(responseError.Errors, fmt.Sprint(elem))
		}
		responseError.Message = strings.Join(responseError.Errors, ", ")
	} else if kind == reflect.Map {
		// A map, parse each error for each key in the map.
		// json always serializes into map[string]interface{} for objects
		for k, v := range shopifyError.Errors.(map[string]interface{}) {
			// Check to make sure the interface is a slice
			// json always serializes JSON arrays into []interface{}
			if reflect.TypeOf(v).Kind() == reflect.Slice {
				for _, elem := range v.([]interface{}) {
					// If the primary message of the response error is not set, use
					// any message.
					if responseError.Message == "" {
						responseError.Message = fmt.Sprint(elem)
					}
					topicAndElem := fmt.Sprintf("%v: %v", k, elem)
					responseError.Errors = append(responseError.Errors, topicAndElem)
				}
			}
		}
	}

	return wrapSpecificError(r, responseError)
}

// General list options that can be used for most collections of entities.
type ListOptions struct {
	Page         int       `url:"page,omitempty"`
	Limit        int       `url:"limit,omitempty"`
	SinceID      int       `url:"since_id,omitempty"`
	CreatedAtMin time.Time `url:"created_at_min,omitempty"`
	CreatedAtMax time.Time `url:"created_at_max,omitempty"`
	UpdatedAtMin time.Time `url:"updated_at_min,omitempty"`
	UpdatedAtMax time.Time `url:"updated_at_max,omitempty"`
	Order        string    `url:"order,omitempty"`
}

// General count options that can be used for most collection counts.
type CountOptions struct {
	CreatedAtMin time.Time `url:"created_at_min,omitempty"`
	CreatedAtMax time.Time `url:"created_at_max,omitempty"`
	UpdatedAtMin time.Time `url:"updated_at_min,omitempty"`
	UpdatedAtMax time.Time `url:"updated_at_max,omitempty"`
}

func (c *Client) Count(path string, options interface{}) (int, error) {
	resource := struct {
		Count int `json:"count"`
	}{}
	err := c.Get(path, &resource, options)
	return resource.Count, err
}

func (c *Client) CreateAndDo(method, path string, data, options, resource interface{}) error {
	req, err := c.NewRequest(method, path, data, options)
	if err != nil {
		return err
	}

	err = c.Do(req, resource)
	if err != nil {
		return err
	}

	return nil
}

// Perform a Get request for the given path and save the result in the given
// resource.
func (c *Client) Get(path string, resource, options interface{}) error {
	return c.CreateAndDo("GET", path, nil, options, resource)
}

// Perform a POST request for the given path and save the result in the given
// resource.
func (c *Client) Post(path string, data, resource interface{}) error {
	return c.CreateAndDo("POST", path, data, nil, resource)
}

// Perform a PUT request for the given path and save the result in the given
// resource.
func (c *Client) Put(path string, data, resource interface{}) error {
	return c.CreateAndDo("PUT", path, data, nil, resource)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"

	"github.com/frenata/deck"
	"github.com/frenata/decktet"
)

// ai is a self-playing gongor whist player
type ai struct {
	name  string
	cards []*decktet.DecktetCard
}

// implementing the deck.Player interface
func (a *ai) String() string      { return a.name + ": " + decktet.ShortPrint(a.cards) }
func (a *ai) Name() string        { return a.name }
func (a *ai) AddCard(c deck.Card) { a.cards = append(a.cards, c.(*decktet.DecktetCard)) }

// play dicates how the ai player will select cards
// heavily in development
func (a *ai) play(g *game, dc *decktet.DecktetCard) *decktet.DecktetCard {
	var i int
	if a.canFollow(dc) {
		for yes := false; !yes; {
			i = a.randCard()
			yes = FollowSuit(dc, a.cards[i])
		}
	} else {
		fmt.Println("can't follow suit")
		i = a.randCard()
	}

	c := a.cards[i]
	a.cards = append(a.cards[:i], a.cards[i+1:]...)
	return c
}

// can the player follow suit from the led card?
func (a *ai) canFollow(dc *decktet.DecktetCard) bool {
	return FollowSuit(dc, a.cards...)
}

// select a random card from hand
func (a *ai) randCard() int {
	return rand.Intn(len(a.cards))
}

// create a new named ai player
func newAi(name string) player {
	a := &ai{name: name}

	a.cards = make([]*decktet.DecktetCard, 0, 7)
	return a
}
<commit_msg>ai now always trys to win, plays the lowest card possible to do so<commit_after>package main

import (
	"errors"
	"fmt"
	"math/rand"

	"github.com/frenata/deck"
	"github.com/frenata/decktet"
)

// ai is a self-playing gongor whist player
type ai struct {
	name  string
	cards []*decktet.DecktetCard
}

// create a new named ai player
func newAi(name string) player {
	a := &ai{name: name}

	a.cards = make([]*decktet.DecktetCard, 0, 7)
	return a
}

// implementing the deck.Player interface
func (a *ai) String() string      { return a.name + ": " + decktet.ShortPrint(a.cards) }
func (a *ai) Name() string        { return a.name }
func (a *ai) AddCard(c deck.Card) { a.cards = append(a.cards, c.(*decktet.DecktetCard)) }

// play dicates how the ai player will select cards
// heavily in development
func (a *ai) play(g *game, dc *decktet.DecktetCard) *decktet.DecktetCard {
	/*var i int
	if a.canFollow(dc) {
		for yes := false; !yes; {
			i = a.randCard()
			yes = FollowSuit(dc, a.cards[i])
		}
	} else {
		fmt.Println("can't follow suit")
		i = a.randCard()
	}

	c := a.cards[i]
	a.cards = append(a.cards[:i], a.cards[i+1:]...)

	return c
	*/

	plays := a.legalPlays(dc)
	win, loss := a.findWinners(dc, g.trump(), plays)
	if len(win) > 1 {
		fmt.Println(win)
	}
	c := decktet.Min(win)
	if c == nil {
		c = decktet.Min(loss)
	}

	if a.pop(c) {
		return c
	}

	return nil
}

func (a *ai) pop(c *decktet.DecktetCard) bool {
	for i, v := range a.cards {
		if c == v {
			//fmt.Println(s)
			a.cards = append(a.cards[:i], a.cards[i+1:]...)
			return true
		}
	}
	return false
}

// can the player follow suit from the led card?
func (a *ai) canFollow(dc *decktet.DecktetCard) bool {
	return FollowSuit(dc, a.cards...)
}

// select a random card from hand
func randCard(cards []*decktet.DecktetCard) (*decktet.DecktetCard, error) {
	if len(cards) == 0 {
		return nil, errors.New("no card available")
	}
	return cards[rand.Intn(len(cards))], nil
}

// Functions for choosing which card to play

// finds all legally playable cards
func (a *ai) legalPlays(dc *decktet.DecktetCard) (results []*decktet.DecktetCard) {
	if a.canFollow(dc) {
		//fmt.Printf("before checking legal plays, a.cards\n%s\n", a.cards)
		for _, c := range a.cards {
			if decktet.SuitMatch(dc, c) {
				results = append(results, c)
			}
		}
		return results
	}
	return a.cards
}

// outputs a set of winning cards and losing cards
func (a *ai) findWinners(dc, ace *decktet.DecktetCard, cards []*decktet.DecktetCard) (win, loss []*decktet.DecktetCard) {
	for _, c := range cards {
		if testWin(dc, c, ace) {
			win = append(win, c)
		}
		loss = append(loss, c)
	}
	return win, loss
}
<|endoftext|>


<commit_before>package gzip

import (
	"compress/gzip"
	"net/http"
	"strings"

	"github.com/codegangsta/negroni"
)

const (
	HeaderAcceptEncoding  = "Accept-Encoding"
	HeaderContentEncoding = "Content-Encoding"
	HeaderContentLength   = "Content-Length"
	HeaderContentType     = "Content-Type"
	HeaderVary            = "Vary"
)

type gzipResponseWriter struct {
	w *gzip.Writer
	negroni.ResponseWriter
}

func (grw gzipResponseWriter) Write(b []byte) (int, error) {
	if len(grw.Header().Get(HeaderContentType)) == 0 {
		grw.Header().Set(HeaderContentType, http.DetectContentType(b))
	}
	return grw.w.Write(b)
}

type Gzipper struct{}

// Returns our Gzipper that adds gzip compression to all requests
func Gzip() *Gzipper {
	return &Gzipper{}
}

func (g *Gzipper) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if !strings.Contains(r.Header.Get(HeaderAcceptEncoding), "gzip") {
		return
	}

	headers := w.Header()
	headers.Set(HeaderContentEncoding, "gzip")
	headers.Set(HeaderVary, HeaderAcceptEncoding)

	gz := gzip.NewWriter(w)
	defer gz.Close()

	gzw := gzipResponseWriter{
		gz,
		w.(negroni.ResponseWriter),
	}

	next(gzw, r)

	// delete content length after we know we have been written to
	gzw.Header().Del(HeaderContentLength)
}
<commit_msg>Fix issue where you didn't get a response if you didn't use gzip.<commit_after>package gzip

import (
	"compress/gzip"
	"net/http"
	"strings"

	"github.com/codegangsta/negroni"
)

const (
	HeaderAcceptEncoding  = "Accept-Encoding"
	HeaderContentEncoding = "Content-Encoding"
	HeaderContentLength   = "Content-Length"
	HeaderContentType     = "Content-Type"
	HeaderVary            = "Vary"
)

type gzipResponseWriter struct {
	w *gzip.Writer
	negroni.ResponseWriter
}

func (grw gzipResponseWriter) Write(b []byte) (int, error) {
	if len(grw.Header().Get(HeaderContentType)) == 0 {
		grw.Header().Set(HeaderContentType, http.DetectContentType(b))
	}
	return grw.w.Write(b)
}

type Gzipper struct{}

// Returns our Gzipper that adds gzip compression to all requests
func Gzip() *Gzipper {
	return &Gzipper{}
}

func (g *Gzipper) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	if !strings.Contains(r.Header.Get(HeaderAcceptEncoding), "gzip") {
		next(w, r)
		return
	}

	headers := w.Header()
	headers.Set(HeaderContentEncoding, "gzip")
	headers.Set(HeaderVary, HeaderAcceptEncoding)

	gz := gzip.NewWriter(w)
	defer gz.Close()

	gzw := gzipResponseWriter{
		gz,
		w.(negroni.ResponseWriter),
	}

	next(gzw, r)

	// delete content length after we know we have been written to
	gzw.Header().Del(HeaderContentLength)
}
<|endoftext|>


<commit_before>package v8eval

import (
	"encoding/json"
	"errors"
	"strings"
)

// V8 is a Go interface for JavaScript engine V8
type V8 interface {
	// Eval evaluates the given JavaScript code 'src' and stores the result into 'res'.
	// The result is marshalled/unmarshalled by using JSON.
	// If the result is undefined, 'res' is not changed.
	// If the result cannot be stored into 'res' due to type mismatch, Eval returns the error.
	// If some JavaScript exception happens in runtime, Eval returns the exception as a Go error.
	Eval(src string, res interface{}) error

	// Call calls the JavaScript function specified by 'fun' with the given argument array 'args'
	// and stores the result into 'res'.
	// The arguments and the result are marshalled/unmarshalled by using JSON.
	// If the result is undefined, 'res' is not changed.
	// If the result cannot be stored into 'res' due to type mismatch, Eval returns the error.
	// If some JavaScript exception happens in runtime, Call returns the exception as a Go error.
	Call(fun string, args interface{}, res interface{}) error

	// EnableDebugger starts a debug server associated with the V8 instance.
	// The server will listen on the given TCP/IP port.
	// If failing to start the server, EnableDebugger returns the error.
	EnableDebugger(port int) error

	// DisableDebugger stops the debug server, if running.
	DisableDebugger()
}

type v8 struct {
	xV8 X_GoV8
}

// NewV8 creates a new V8 instance.
func NewV8() V8 {
	v := new(v8)
	v.xV8 = NewX_GoV8()
	return v
}

func (v *v8) Eval(src string, res interface{}) error {
	return v.decode(v.xV8.Eval(src), res)
}

func (v *v8) Call(fun string, args interface{}, res interface{}) error {
	as, err := json.Marshal(args)
	if err != nil {
		return err
	}

	return v.decode(v.xV8.Call(fun, string(as)), res)
}

func (v *v8) decode(str string, val interface{}) error {
	if str == "undefined" {
		return nil
	}

	dec := json.NewDecoder(strings.NewReader(str))
	err := dec.Decode(val)
	if err != nil {
		if strings.HasPrefix(err.Error(), "json: ") {
			return err
		}

		return errors.New(str)
	}

	return nil
}

func (v *v8) EnableDebugger(port int) error {
	if !v.xV8.Enable_debugger(port) {
		return errors.New("failed to start debug server")
	}

	return nil
}

func (v *v8) DisableDebugger() {
	v.xV8.Disable_debugger()
}
<commit_msg>Set the finalizer for v8 objects<commit_after>package v8eval

import (
	"encoding/json"
	"errors"
	"runtime"
	"strings"
)

// V8 is a Go interface for JavaScript engine V8
type V8 interface {
	// Eval evaluates the given JavaScript code 'src' and stores the result into 'res'.
	// The result is marshalled/unmarshalled by using JSON.
	// If the result is undefined, 'res' is not changed.
	// If the result cannot be stored into 'res' due to type mismatch, Eval returns the error.
	// If some JavaScript exception happens in runtime, Eval returns the exception as a Go error.
	Eval(src string, res interface{}) error

	// Call calls the JavaScript function specified by 'fun' with the given argument array 'args'
	// and stores the result into 'res'.
	// The arguments and the result are marshalled/unmarshalled by using JSON.
	// If the result is undefined, 'res' is not changed.
	// If the result cannot be stored into 'res' due to type mismatch, Eval returns the error.
	// If some JavaScript exception happens in runtime, Call returns the exception as a Go error.
	Call(fun string, args interface{}, res interface{}) error

	// EnableDebugger starts a debug server associated with the V8 instance.
	// The server will listen on the given TCP/IP port.
	// If failing to start the server, EnableDebugger returns the error.
	EnableDebugger(port int) error

	// DisableDebugger stops the debug server, if running.
	DisableDebugger()
}

type v8 struct {
	xV8 X_GoV8
}

// NewV8 creates a new V8 instance.
func NewV8() V8 {
	v := new(v8)
	v.xV8 = NewX_GoV8()
	runtime.SetFinalizer(v, deleteV8)
	return v
}

func deleteV8(v *v8) {
	DeleteX_GoV8(v.xV8)
	v.xV8 = nil
}

func (v *v8) Eval(src string, res interface{}) error {
	return v.decode(v.xV8.Eval(src), res)
}

func (v *v8) Call(fun string, args interface{}, res interface{}) error {
	as, err := json.Marshal(args)
	if err != nil {
		return err
	}

	return v.decode(v.xV8.Call(fun, string(as)), res)
}

func (v *v8) decode(str string, val interface{}) error {
	if str == "undefined" {
		return nil
	}

	dec := json.NewDecoder(strings.NewReader(str))
	err := dec.Decode(val)
	if err != nil {
		if strings.HasPrefix(err.Error(), "json: ") {
			return err
		}

		return errors.New(str)
	}

	return nil
}

func (v *v8) EnableDebugger(port int) error {
	if !v.xV8.Enable_debugger(port) {
		return errors.New("failed to start debug server")
	}

	return nil
}

func (v *v8) DisableDebugger() {
	v.xV8.Disable_debugger()
}
<|endoftext|>


<commit_before>package buildinfo

import (
	"encoding/base64"
	"fmt"
	"gopkg.in/hlandau/easyconfig.v1/cflag"
	"os"
	"runtime"
	"strings"
)

// Full build info.
var BuildInfo string

// Set via go build.
var RawBuildInfo string

// Program-settable extra version information to print.
var Extra string

func init() {
	versionFlag := cflag.Bool(nil, "version", false, "Print version information")
	versionFlag.RegisterOnChange(func(bf *cflag.BoolFlag) {
		if !bf.Value() {
			return
		}

		fmt.Print(Full())
		os.Exit(2)
	})

	if RawBuildInfo == "" || BuildInfo != "" {
		return
	}

	b, err := base64.RawStdEncoding.DecodeString(strings.TrimRight(RawBuildInfo, "="))
	if err != nil {
		return
	}

	BuildInfo = string(b)
}

func Full() string {
	bi := BuildInfo
	if bi == "" {
		bi = "build unknown"
	}
	return fmt.Sprintf("%sgo version %s %s/%s %s cgo=%v\n%s\n", Extra, runtime.Version(), runtime.GOOS, runtime.GOARCH, runtime.Compiler, Cgo, bi)
}
<commit_msg>Move buildinfo to hlandau/buildinfo<commit_after>package buildinfo

import (
	"encoding/base64"
	"fmt"
	"github.com/hlandau/buildinfo"
	"gopkg.in/hlandau/easyconfig.v1/cflag"
	"os"
	"runtime"
	"strings"
)

// Full build info.
var BuildInfo string

// Set via go build.
var RawBuildInfo string

// Program-settable extra version information to print.
var Extra string

func init() {
	if RawBuildInfo != "" {
		buildinfo.RawBuildInfo = RawBuildInfo
	}
	if BuildInfo != "" {
		buildinfo.BuildInfo = BuildInfo
	}
	if Extra != "" {
		buildinfo.Extra = Extra
	}
	buildinfo.Update()
	RawBuildInfo = buildinfo.RawBuildInfo
	BuildInfo = buildinfo.BuildInfo
	Extra = buildinfo.Extra
}

func Full() string {
	return buildinfo.Full()
}
<|endoftext|>


<commit_before>package buildinfo

import "encoding/base64"
import "gopkg.in/hlandau/easyconfig.v1/cflag"
import "os"
import "fmt"
import "runtime"

// Full build info.
var BuildInfo string

// Set via go build.
var RawBuildInfo string

// Program-settable extra version information to print.
var Extra string

func init() {
	versionFlag := cflag.Bool(nil, "version", false, "Print version information")
	versionFlag.RegisterOnChange(func(bf *cflag.BoolFlag) {
		if !bf.Value() {
			return
		}

		fmt.Fprint(os.Stderr, Full())
		os.Exit(2)
	})

	if RawBuildInfo == "" {
		return
	}

	b, err := base64.StdEncoding.DecodeString(RawBuildInfo)
	if err != nil {
		return
	}

	BuildInfo = string(b)
}

func Full() string {
	bi := BuildInfo
	if bi == "" {
		bi = "build unknown"
	}
	return fmt.Sprintf("%sgo version %s %s/%s %s cgo=%v\n%s\n", Extra, runtime.Version(), runtime.GOOS, runtime.GOARCH, runtime.Compiler, Cgo, bi)
}
<commit_msg>buildinfo<commit_after>package buildinfo

import (
	"encoding/base64"
	"fmt"
	"gopkg.in/hlandau/easyconfig.v1/cflag"
	"os"
	"runtime"
	"strings"
)

// Full build info.
var BuildInfo string

// Set via go build.
var RawBuildInfo string

// Program-settable extra version information to print.
var Extra string

func init() {
	versionFlag := cflag.Bool(nil, "version", false, "Print version information")
	versionFlag.RegisterOnChange(func(bf *cflag.BoolFlag) {
		if !bf.Value() {
			return
		}

		fmt.Fprint(os.Stderr, Full())
		os.Exit(2)
	})

	if RawBuildInfo == "" || BuildInfo != "" {
		return
	}

	b, err := base64.RawStdEncoding.DecodeString(strings.TrimRight(RawBuildInfo, "="))
	if err != nil {
		return
	}

	BuildInfo = string(b)
}

func Full() string {
	bi := BuildInfo
	if bi == "" {
		bi = "build unknown"
	}
	return fmt.Sprintf("%sgo version %s %s/%s %s cgo=%v\n%s\n", Extra, runtime.Version(), runtime.GOOS, runtime.GOARCH, runtime.Compiler, Cgo, bi)
}
<|endoftext|>


<commit_before>// Copyright 2013 Google Inc.  All rights reserved.
//
// 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.

// rawread attempts to read from the specified USB device.
package main

import (
	"flag"
	"fmt"
	"log"

	"github.com/kylelemons/gousb/usb"
	"github.com/kylelemons/gousb/usbid"
)

var (
	device   = flag.String("device", "03eb:2013", "Device to which to connect")
	config   = flag.Int("config", 1, "Endpoint to which to connect")
	iface    = flag.Int("interface", 0, "Endpoint to which to connect")
	setup    = flag.Int("setup", 0, "Endpoint to which to connect")
	endpoint = flag.Int("endpoint", 1, "Endpoint to which to connect")
	debug    = flag.Int("debug", 3, "Debug level for libusb")
)

func main() {
	flag.Parse()

	// Only one context should be needed for an application.  It should always be closed.
	ctx := usb.NewContext()
	defer ctx.Close()

	ctx.Debug(*debug)

	log.Printf("Scanning for device %q...", *device)

	// ListDevices is used to find the devices to open.
	devs, err := ctx.ListDevices(func(desc *usb.Descriptor) bool {
		if fmt.Sprintf("%s:%s", desc.Vendor, desc.Product) != *device {
			return false
		}

		// The usbid package can be used to print out human readable information.
		fmt.Printf("  Protocol: %s\n", usbid.Classify(desc))

		// The configurations can be examined from the Descriptor, though they can only
		// be set once the device is opened.  All configuration references must be closed,
		// to free up the memory in libusb.
		for _, cfg := range desc.Configs {
			// This loop just uses more of the built-in and usbid pretty printing to list
			// the USB devices.
			fmt.Printf("  %s:\n", cfg)
			for _, alt := range cfg.Interfaces {
				fmt.Printf("    --------------\n")
				for _, iface := range alt.Setups {
					fmt.Printf("    %s\n", iface)
					fmt.Printf("      %s\n", usbid.Classify(iface))
					for _, end := range iface.Endpoints {
						fmt.Printf("      %s\n", end)
					}
				}
			}
			fmt.Printf("    --------------\n")
		}

		return true
	})

	// All Devices returned from ListDevices must be closed.
	defer func() {
		for _, d := range devs {
			d.Close()
		}
	}()

	// ListDevices can occaionally fail, so be sure to check its return value.
	if err != nil {
		log.Fatalf("list: %s", err)
	}

	if len(devs) == 0 {
		log.Fatalf("no devices found")
	}

	dev := devs[0]

	log.Printf("Connecting to endpoint...")
	log.Printf("- %#v", dev.Descriptor)
	ep, err := dev.OpenEndpoint(uint8(*config), uint8(*iface), uint8(*setup), uint8(*endpoint)|uint8(usb.ENDPOINT_DIR_IN))
	if err != nil {
		log.Fatalf("open: %s", err)
	}
	_ = ep
}
<commit_msg>beginning to port c client<commit_after>// Copyright 2013 Google Inc.  All rights reserved.
//
// 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.

// rawread attempts to read from the specified USB device.
package main

import (
	"flag"
	"fmt"
	"github.com/kylelemons/gousb/usb"
	"github.com/kylelemons/gousb/usbid"
	"log"
)

var (
	device   = flag.String("device", "03eb:2013", "Device to which to connect")
	config   = flag.Int("config", 1, "Endpoint to which to connect")
	iface    = flag.Int("interface", 0, "Endpoint to which to connect")
	setup    = flag.Int("setup", 0, "Endpoint to which to connect")
	endpoint = flag.Int("endpoint", 1, "Endpoint to which to connect")
	debug    = flag.Int("debug", 3, "Debug level for libusb")
)

func main() {
	flag.Parse()

	// Only one context should be needed for an application.  It should always be closed.
	ctx := usb.NewContext()
	defer ctx.Close()

	ctx.Debug(*debug)

	log.Printf("Scanning for device %q...", *device)

	// ListDevices is used to find the devices to open.
	devs, err := ctx.ListDevices(func(desc *usb.Descriptor) bool {
		if fmt.Sprintf("%s:%s", desc.Vendor, desc.Product) != *device {
			return false
		}

		// The usbid package can be used to print out human readable information.
		fmt.Printf("  Protocol: %s\n", usbid.Classify(desc))

		// The configurations can be examined from the Descriptor, though they can only
		// be set once the device is opened.  All configuration references must be closed,
		// to free up the memory in libusb.
		for _, cfg := range desc.Configs {
			// This loop just uses more of the built-in and usbid pretty printing to list
			// the USB devices.
			fmt.Printf("  %s:\n", cfg)
			for _, alt := range cfg.Interfaces {
				fmt.Printf("    --------------\n")
				for _, iface := range alt.Setups {
					fmt.Printf("    %s\n", iface)
					fmt.Printf("      %s\n", usbid.Classify(iface))
					for _, end := range iface.Endpoints {
						fmt.Printf("      %s\n", end)
					}
				}
			}
			fmt.Printf("    --------------\n")
		}

		return true
	})

	// All Devices returned from ListDevices must be closed.
	defer func() {
		for _, d := range devs {
			d.Close()
		}
	}()

	// ListDevices can occaionally fail, so be sure to check its return value.
	if err != nil {
		log.Fatalf("list: %s", err)
	}

	if len(devs) == 0 {
		log.Fatalf("no devices found")
	}

	dev := devs[0]

	log.Printf("Connecting to endpoint...")
	log.Printf("- %#v", dev.Descriptor)
	ep, err := dev.OpenEndpoint(uint8(*config), uint8(*iface), uint8(*setup), uint8(*endpoint)|uint8(usb.ENDPOINT_DIR_IN))
	if err != nil {
		log.Fatalf("open: %s", err)
	}
	log.Printf("Got Endpoint: %#v", ep)

	var buf []byte

	// Read invalid bytes from device
	num, err := ep.Read(buf)
	if err != nil {
		log.Fatal("Failed to read pending bytes into buffer")
	}
	log.Printf("Read %d bytes into temporary buffer", num)

}
<|endoftext|>


<commit_before>// Copyright (c) 2018-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package txscript

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"testing"

	"github.com/decred/dcrd/wire"
)

var (
	// manyInputsBenchTx is a transaction that contains a lot of inputs which is
	// useful for benchmarking signature hash calculation.
	manyInputsBenchTx wire.MsgTx

	// A mock previous output script to use in the signing benchmark.
	prevOutScript = hexToBytes("a914f5916158e3e2c4551c1796708db8367207ed13bb87")
)

func init() {
	// tx 620f57c92cf05a7f7e7f7d28255d5f7089437bc48e34dcfebf7751d08b7fb8f5
	txHex, err := ioutil.ReadFile("data/many_inputs_tx.hex")
	if err != nil {
		panic(fmt.Sprintf("unable to read benchmark tx file: %v", err))
	}

	txBytes := hexToBytes(string(txHex))
	err = manyInputsBenchTx.Deserialize(bytes.NewReader(txBytes))
	if err != nil {
		panic(err)
	}
}

// BenchmarkCalcSigHash benchmarks how long it takes to calculate the signature
// hashes for all inputs of a transaction with many inputs.
func BenchmarkCalcSigHash(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for j := 0; j < len(manyInputsBenchTx.TxIn); j++ {
			_, err := CalcSignatureHash(prevOutScript, SigHashAll,
				&manyInputsBenchTx, j, nil)
			if err != nil {
				b.Fatalf("failed to calc signature hash: %v", err)
			}
		}
	}
}

// genComplexScript returns a script comprised of half as many opcodes as the
// maximum allowed followed by as many max size data pushes fit without
// exceeding the max allowed script size.
func genComplexScript() ([]byte, error) {
	var scriptLen int
	builder := NewScriptBuilder()
	for i := 0; i < MaxOpsPerScript/2; i++ {
		builder.AddOp(OP_TRUE)
		scriptLen++
	}
	maxData := bytes.Repeat([]byte{0x02}, MaxScriptElementSize)
	for i := 0; i < (MaxScriptSize-scriptLen)/MaxScriptElementSize; i++ {
		builder.AddData(maxData)
	}
	return builder.Script()
}

// BenchmarkScriptParsing benchmarks how long it takes to parse a very large
// script.
func BenchmarkScriptParsing(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	const scriptVersion = 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tokenizer := MakeScriptTokenizer(scriptVersion, script)
		for tokenizer.Next() {
			_ = tokenizer.Opcode()
			_ = tokenizer.Data()
			_ = tokenizer.ByteIndex()
		}
		if err := tokenizer.Err(); err != nil {
			b.Fatalf("failed to parse script: %v", err)
		}
	}
}

// BenchmarkDisasmString benchmarks how long it takes to disassemble a very
// large script.
func BenchmarkDisasmString(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := DisasmString(script)
		if err != nil {
			b.Fatalf("failed to disasm script: %v", err)
		}
	}
}

// BenchmarkIsPayToScriptHash benchmarks how long it takes IsPayToScriptHash to
// analyze a very large script.
func BenchmarkIsPayToScriptHash(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = IsPayToScriptHash(script)
	}
}

// BenchmarkIsMultisigScriptLarge benchmarks how long it takes IsMultisigScript
// to analyze a very large script.
func BenchmarkIsMultisigScriptLarge(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		isMultisig, err := IsMultisigScript(script)
		if err != nil {
			b.Fatalf("unexpected err: %v", err)
		}
		if isMultisig {
			b.Fatalf("script should NOT be reported as mutisig script")
		}
	}
}

// BenchmarkIsMultisigScript benchmarks how long it takes IsMultisigScript to
// analyze a 1-of-2 multisig public key script.
func BenchmarkIsMultisigScript(b *testing.B) {
	multisigShortForm := "1 " +
		"DATA_33 " +
		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
		"DATA_33 " +
		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
		"2 CHECKMULTISIG"
	pkScript := mustParseShortForm(multisigShortForm)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		isMultisig, err := IsMultisigScript(pkScript)
		if err != nil {
			b.Fatalf("unexpected err: %v", err)
		}
		if !isMultisig {
			b.Fatalf("script should be reported as a mutisig script")
		}
	}
}

// BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
// to analyze a very large script.
func BenchmarkIsMultisigSigScriptLarge(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if IsMultisigSigScript(script) {
			b.Fatalf("script should NOT be reported as mutisig sig script")
		}
	}
}

// BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
// to analyze both a 1-of-2 multisig public key script (which should be false)
// and a signature script comprised of a pay-to-script-hash 1-of-2 multisig
// redeem script (which should be true).
func BenchmarkIsMultisigSigScript(b *testing.B) {
	multisigShortForm := "1 " +
		"DATA_33 " +
		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
		"DATA_33 " +
		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
		"2 CHECKMULTISIG"
	pkScript := mustParseShortForm(multisigShortForm)

	sigHex := "0x304402205795c3ab6ba11331eeac757bf1fc9c34bef0c7e1a9c8bd5eebb8" +
		"82f3b79c5838022001e0ab7b4c7662e4522dc5fa479e4b4133fa88c6a53d895dc1d5" +
		"2eddc7bbcf2801 "
	sigScript := mustParseShortForm("DATA_71 " + sigHex + "DATA_71 " +
		multisigShortForm)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if IsMultisigSigScript(pkScript) {
			b.Fatalf("script should NOT be reported as mutisig sig script")
		}
		if !IsMultisigSigScript(sigScript) {
			b.Fatalf("script should be reported as a mutisig sig script")
		}
	}
}
<commit_msg>txscript: Add benchmark for GetSigOpCount.<commit_after>// Copyright (c) 2018-2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package txscript

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"testing"

	"github.com/decred/dcrd/wire"
)

var (
	// manyInputsBenchTx is a transaction that contains a lot of inputs which is
	// useful for benchmarking signature hash calculation.
	manyInputsBenchTx wire.MsgTx

	// A mock previous output script to use in the signing benchmark.
	prevOutScript = hexToBytes("a914f5916158e3e2c4551c1796708db8367207ed13bb87")
)

func init() {
	// tx 620f57c92cf05a7f7e7f7d28255d5f7089437bc48e34dcfebf7751d08b7fb8f5
	txHex, err := ioutil.ReadFile("data/many_inputs_tx.hex")
	if err != nil {
		panic(fmt.Sprintf("unable to read benchmark tx file: %v", err))
	}

	txBytes := hexToBytes(string(txHex))
	err = manyInputsBenchTx.Deserialize(bytes.NewReader(txBytes))
	if err != nil {
		panic(err)
	}
}

// BenchmarkCalcSigHash benchmarks how long it takes to calculate the signature
// hashes for all inputs of a transaction with many inputs.
func BenchmarkCalcSigHash(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for j := 0; j < len(manyInputsBenchTx.TxIn); j++ {
			_, err := CalcSignatureHash(prevOutScript, SigHashAll,
				&manyInputsBenchTx, j, nil)
			if err != nil {
				b.Fatalf("failed to calc signature hash: %v", err)
			}
		}
	}
}

// genComplexScript returns a script comprised of half as many opcodes as the
// maximum allowed followed by as many max size data pushes fit without
// exceeding the max allowed script size.
func genComplexScript() ([]byte, error) {
	var scriptLen int
	builder := NewScriptBuilder()
	for i := 0; i < MaxOpsPerScript/2; i++ {
		builder.AddOp(OP_TRUE)
		scriptLen++
	}
	maxData := bytes.Repeat([]byte{0x02}, MaxScriptElementSize)
	for i := 0; i < (MaxScriptSize-scriptLen)/MaxScriptElementSize; i++ {
		builder.AddData(maxData)
	}
	return builder.Script()
}

// BenchmarkScriptParsing benchmarks how long it takes to parse a very large
// script.
func BenchmarkScriptParsing(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	const scriptVersion = 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tokenizer := MakeScriptTokenizer(scriptVersion, script)
		for tokenizer.Next() {
			_ = tokenizer.Opcode()
			_ = tokenizer.Data()
			_ = tokenizer.ByteIndex()
		}
		if err := tokenizer.Err(); err != nil {
			b.Fatalf("failed to parse script: %v", err)
		}
	}
}

// BenchmarkDisasmString benchmarks how long it takes to disassemble a very
// large script.
func BenchmarkDisasmString(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := DisasmString(script)
		if err != nil {
			b.Fatalf("failed to disasm script: %v", err)
		}
	}
}

// BenchmarkIsPayToScriptHash benchmarks how long it takes IsPayToScriptHash to
// analyze a very large script.
func BenchmarkIsPayToScriptHash(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = IsPayToScriptHash(script)
	}
}

// BenchmarkIsMultisigScriptLarge benchmarks how long it takes IsMultisigScript
// to analyze a very large script.
func BenchmarkIsMultisigScriptLarge(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		isMultisig, err := IsMultisigScript(script)
		if err != nil {
			b.Fatalf("unexpected err: %v", err)
		}
		if isMultisig {
			b.Fatalf("script should NOT be reported as mutisig script")
		}
	}
}

// BenchmarkIsMultisigScript benchmarks how long it takes IsMultisigScript to
// analyze a 1-of-2 multisig public key script.
func BenchmarkIsMultisigScript(b *testing.B) {
	multisigShortForm := "1 " +
		"DATA_33 " +
		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
		"DATA_33 " +
		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
		"2 CHECKMULTISIG"
	pkScript := mustParseShortForm(multisigShortForm)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		isMultisig, err := IsMultisigScript(pkScript)
		if err != nil {
			b.Fatalf("unexpected err: %v", err)
		}
		if !isMultisig {
			b.Fatalf("script should be reported as a mutisig script")
		}
	}
}

// BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
// to analyze a very large script.
func BenchmarkIsMultisigSigScriptLarge(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if IsMultisigSigScript(script) {
			b.Fatalf("script should NOT be reported as mutisig sig script")
		}
	}
}

// BenchmarkIsMultisigSigScript benchmarks how long it takes IsMultisigSigScript
// to analyze both a 1-of-2 multisig public key script (which should be false)
// and a signature script comprised of a pay-to-script-hash 1-of-2 multisig
// redeem script (which should be true).
func BenchmarkIsMultisigSigScript(b *testing.B) {
	multisigShortForm := "1 " +
		"DATA_33 " +
		"0x030478aaaa2be30772f1e69e581610f1840b3cf2fe7228ee0281cd599e5746f81e " +
		"DATA_33 " +
		"0x0284f4d078b236a9ff91661f8ffbe012737cd3507566f30fd97d25f2b23539f3cd " +
		"2 CHECKMULTISIG"
	pkScript := mustParseShortForm(multisigShortForm)

	sigHex := "0x304402205795c3ab6ba11331eeac757bf1fc9c34bef0c7e1a9c8bd5eebb8" +
		"82f3b79c5838022001e0ab7b4c7662e4522dc5fa479e4b4133fa88c6a53d895dc1d5" +
		"2eddc7bbcf2801 "
	sigScript := mustParseShortForm("DATA_71 " + sigHex + "DATA_71 " +
		multisigShortForm)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		if IsMultisigSigScript(pkScript) {
			b.Fatalf("script should NOT be reported as mutisig sig script")
		}
		if !IsMultisigSigScript(sigScript) {
			b.Fatalf("script should be reported as a mutisig sig script")
		}
	}
}

// BenchmarkGetSigOpCount benchmarks how long it takes to count the signature
// operations of a very large script.
func BenchmarkGetSigOpCount(b *testing.B) {
	script, err := genComplexScript()
	if err != nil {
		b.Fatalf("failed to create benchmark script: %v", err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = GetSigOpCount(script)
	}
}
<|endoftext|>


<commit_before>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package common

import (
	"context"
	"reflect"
	"strings"

	"github.com/dop251/goja"
	"github.com/pkg/errors"
	"github.com/serenize/snaker"
)

// The field name mapper translates Go symbol names for bridging to JS.
type FieldNameMapper struct{}

// Bridge exported fields, snake_casing their names. A `js:"name"` tag overrides, `js:"-"` hides.
func (FieldNameMapper) FieldName(t reflect.Type, f reflect.StructField) string {
	// PkgPath is non-empty for unexported fields.
	if f.PkgPath != "" {
		return ""
	}

	// Allow a `js:"name"` tag to override the default name.
	if tag := f.Tag.Get("js"); tag != "" {
		// Matching encoding/json, `js:"-"` hides a field.
		if tag == "-" {
			return ""
		}
		return tag
	}

	// Default to lowercasing the first character of the field name.
	return snaker.CamelToSnake(f.Name)
}

// Bridge exported methods, but camelCase their names.
func (FieldNameMapper) MethodName(t reflect.Type, m reflect.Method) string {
	// PkgPath is non-empty for unexported methods.
	if m.PkgPath != "" {
		return ""
	}

	// Lowercase the first character of the method name.
	return strings.ToLower(m.Name[0:1]) + m.Name[1:]
}

// Binds an object's members to the global scope. Returns a function that un-binds them.
// Note that this will panic if passed something that isn't a struct; please don't do that.
func BindToGlobal(rt *goja.Runtime, v interface{}) func() {
	mapper := FieldNameMapper{}
	keys := []string{}

	val := reflect.ValueOf(v)
	typ := val.Type()
	for i := 0; i < typ.NumMethod(); i++ {
		m := typ.Method(i)
		k := mapper.MethodName(typ, m)
		if k != "" {
			fn := val.Method(i).Interface()
			keys = append(keys, k)
			rt.Set(k, fn)
		}
	}

	elem := val
	elemTyp := typ
	if typ.Kind() == reflect.Ptr {
		elem = val.Elem()
		elemTyp = elem.Type()
	}
	for i := 0; i < elemTyp.NumField(); i++ {
		f := elemTyp.Field(i)
		k := mapper.FieldName(elemTyp, f)
		if k != "" {
			v := elem.Field(i).Interface()
			keys = append(keys, k)
			rt.Set(k, v)
		}
	}

	return func() {
		for _, k := range keys {
			rt.Set(k, goja.Undefined())
		}
	}
}

func Bind(rt *goja.Runtime, v interface{}, ctx *context.Context) goja.Value {
	ctxT := reflect.TypeOf((*context.Context)(nil)).Elem()
	errorT := reflect.TypeOf((*error)(nil)).Elem()

	exports := rt.NewObject()
	mapper := FieldNameMapper{}

	val := reflect.ValueOf(v)
	typ := val.Type()
	for i := 0; i < typ.NumMethod(); i++ {
		i := i
		methT := typ.Method(i)
		name := mapper.MethodName(typ, methT)
		if name == "" {
			continue
		}
		meth := val.Method(i)

		in := make([]reflect.Type, methT.Type.NumIn())
		for i := 0; i < len(in); i++ {
			in[i] = methT.Type.In(i)
		}
		out := make([]reflect.Type, methT.Type.NumOut())
		for i := 0; i < len(out); i++ {
			out[i] = methT.Type.Out(i)
		}

		// Skip over the first input arg; it'll be the bound object.
		in = in[1:]

		// If the first argument is a context.Context, inject the given context.
		// The function will error if called outside of a valid context.
		if len(in) > 0 && in[0].Implements(ctxT) {
			in = in[1:]
			meth = bindContext(in, out, methT, meth, rt, ctx)
		}

		// If the last return value is an error, turn it into a JS throw.
		if len(out) > 0 && out[len(out)-1] == errorT {
			out = out[:len(out)-1]
			meth = bindErrorHandler(in, out, methT, meth, rt)
		}

		_ = exports.Set(name, meth.Interface())
	}

	elem := val
	elemTyp := typ
	if typ.Kind() == reflect.Ptr {
		elem = val.Elem()
		elemTyp = elem.Type()
	}
	for i := 0; i < elemTyp.NumField(); i++ {
		f := elemTyp.Field(i)
		k := mapper.FieldName(elemTyp, f)
		if k == "" {
			continue
		}
		_ = exports.Set(k, elem.Field(i).Interface())
	}

	return rt.ToValue(exports)
}

func bindContext(in, out []reflect.Type, methT reflect.Method, meth reflect.Value, rt *goja.Runtime, ctxPtr *context.Context) reflect.Value {
	return reflect.MakeFunc(
		reflect.FuncOf(in, out, methT.Type.IsVariadic()),
		func(args []reflect.Value) []reflect.Value {
			if ctxPtr == nil || *ctxPtr == nil {
				Throw(rt, errors.Errorf("%s needs a valid VU context", methT.Name))
			}
			ctx := *ctxPtr

			select {
			case <-ctx.Done():
				Throw(rt, errors.Errorf("test has ended"))
			default:
			}

			return callBound(methT, meth, append([]reflect.Value{reflect.ValueOf(ctx)}, args...))
		},
	)
}

func bindErrorHandler(in, out []reflect.Type, methT reflect.Method, meth reflect.Value, rt *goja.Runtime) reflect.Value {
	return reflect.MakeFunc(
		reflect.FuncOf(in, out, methT.Type.IsVariadic()),
		func(args []reflect.Value) []reflect.Value {
			ret := callBound(methT, meth, args)
			err := ret[len(ret)-1]
			if !err.IsNil() {
				Throw(rt, err.Interface().(error))
			}
			return ret[:len(ret)-1]
		},
	)
}

func callBound(methT reflect.Method, meth reflect.Value, args []reflect.Value) []reflect.Value {
	if methT.Type.IsVariadic() {
		return meth.CallSlice(args)
	}
	return meth.Call(args)
}
<commit_msg>Bind speedup: work with maps Go-side, not Objects<commit_after>/*
 *
 * k6 - a next-generation load testing tool
 * Copyright (C) 2016 Load Impact
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package common

import (
	"context"
	"reflect"
	"strings"

	"github.com/dop251/goja"
	"github.com/pkg/errors"
	"github.com/serenize/snaker"
)

var (
	ctxT   = reflect.TypeOf((*context.Context)(nil)).Elem()
	errorT = reflect.TypeOf((*error)(nil)).Elem()
	mapper = FieldNameMapper{}
)

// The field name mapper translates Go symbol names for bridging to JS.
type FieldNameMapper struct{}

// Bridge exported fields, snake_casing their names. A `js:"name"` tag overrides, `js:"-"` hides.
func (FieldNameMapper) FieldName(t reflect.Type, f reflect.StructField) string {
	// PkgPath is non-empty for unexported fields.
	if f.PkgPath != "" {
		return ""
	}

	// Allow a `js:"name"` tag to override the default name.
	if tag := f.Tag.Get("js"); tag != "" {
		// Matching encoding/json, `js:"-"` hides a field.
		if tag == "-" {
			return ""
		}
		return tag
	}

	// Default to lowercasing the first character of the field name.
	return snaker.CamelToSnake(f.Name)
}

// Bridge exported methods, but camelCase their names.
func (FieldNameMapper) MethodName(t reflect.Type, m reflect.Method) string {
	// PkgPath is non-empty for unexported methods.
	if m.PkgPath != "" {
		return ""
	}

	// Lowercase the first character of the method name.
	return strings.ToLower(m.Name[0:1]) + m.Name[1:]
}

// Binds an object's members to the global scope. Returns a function that un-binds them.
// Note that this will panic if passed something that isn't a struct; please don't do that.
func BindToGlobal(rt *goja.Runtime, v interface{}) func() {
	keys := []string{}

	val := reflect.ValueOf(v)
	typ := val.Type()
	for i := 0; i < typ.NumMethod(); i++ {
		m := typ.Method(i)
		k := mapper.MethodName(typ, m)
		if k != "" {
			fn := val.Method(i).Interface()
			keys = append(keys, k)
			rt.Set(k, fn)
		}
	}

	elem := val
	elemTyp := typ
	if typ.Kind() == reflect.Ptr {
		elem = val.Elem()
		elemTyp = elem.Type()
	}
	for i := 0; i < elemTyp.NumField(); i++ {
		f := elemTyp.Field(i)
		k := mapper.FieldName(elemTyp, f)
		if k != "" {
			v := elem.Field(i).Interface()
			keys = append(keys, k)
			rt.Set(k, v)
		}
	}

	return func() {
		for _, k := range keys {
			rt.Set(k, goja.Undefined())
		}
	}
}

func Bind(rt *goja.Runtime, v interface{}, ctx *context.Context) goja.Value {
	val := reflect.ValueOf(v)
	typ := val.Type()
	exports := make(map[string]interface{})
	for i := 0; i < typ.NumMethod(); i++ {
		methT := typ.Method(i)
		name := mapper.MethodName(typ, methT)
		if name == "" {
			continue
		}
		meth := val.Method(i)

		in := make([]reflect.Type, methT.Type.NumIn())
		for i := 0; i < len(in); i++ {
			in[i] = methT.Type.In(i)
		}
		out := make([]reflect.Type, methT.Type.NumOut())
		for i := 0; i < len(out); i++ {
			out[i] = methT.Type.Out(i)
		}

		// Skip over the first input arg; it'll be the bound object.
		in = in[1:]

		// If the first argument is a context.Context, inject the given context.
		// The function will error if called outside of a valid context.
		if len(in) > 0 && in[0].Implements(ctxT) {
			in = in[1:]
			meth = bindContext(in, out, methT, meth, rt, ctx)
		}

		// If the last return value is an error, turn it into a JS throw.
		if len(out) > 0 && out[len(out)-1] == errorT {
			out = out[:len(out)-1]
			meth = bindErrorHandler(in, out, methT, meth, rt)
		}

		exports[name] = meth.Interface()
	}

	elem := val
	elemTyp := typ
	if typ.Kind() == reflect.Ptr {
		elem = val.Elem()
		elemTyp = elem.Type()
	}
	for i := 0; i < elemTyp.NumField(); i++ {
		f := elemTyp.Field(i)
		k := mapper.FieldName(elemTyp, f)
		if k == "" {
			continue
		}
		exports[k] = elem.Field(i).Interface()
	}

	return rt.ToValue(exports)
}

func bindContext(in, out []reflect.Type, methT reflect.Method, meth reflect.Value, rt *goja.Runtime, ctxPtr *context.Context) reflect.Value {
	return reflect.MakeFunc(
		reflect.FuncOf(in, out, methT.Type.IsVariadic()),
		func(args []reflect.Value) []reflect.Value {
			if ctxPtr == nil || *ctxPtr == nil {
				Throw(rt, errors.Errorf("%s needs a valid VU context", methT.Name))
			}
			ctx := *ctxPtr

			select {
			case <-ctx.Done():
				Throw(rt, errors.Errorf("test has ended"))
			default:
			}

			return callBound(methT, meth, append([]reflect.Value{reflect.ValueOf(ctx)}, args...))
		},
	)
}

func bindErrorHandler(in, out []reflect.Type, methT reflect.Method, meth reflect.Value, rt *goja.Runtime) reflect.Value {
	return reflect.MakeFunc(
		reflect.FuncOf(in, out, methT.Type.IsVariadic()),
		func(args []reflect.Value) []reflect.Value {
			ret := callBound(methT, meth, args)
			err := ret[len(ret)-1]
			if !err.IsNil() {
				Throw(rt, err.Interface().(error))
			}
			return ret[:len(ret)-1]
		},
	)
}

func callBound(methT reflect.Method, meth reflect.Value, args []reflect.Value) []reflect.Value {
	if methT.Type.IsVariadic() {
		return meth.CallSlice(args)
	}
	return meth.Call(args)
}
<|endoftext|>


<commit_before>package cachet_test

import (
	"fmt"
	"github.com/andygrunwald/cachet"
)

func ExampleTrending_Ping() {
	client, err:= cachet.NewClient("https://demo.cachethq.io/", nil)
	if err != nil {
		panic(err)
	}

	pong, resp, err := client.General.Ping()
	if err != nil {
		panic(err)
	}

	fmt.Printf("Result: %s\n", pong)
	fmt.Printf("Status: %s\n", resp.Status)

	// Output: Result: Pong!
	// Status: 200 OK
}

func ExampleTrending_CreateComponent() {
	client, err:= cachet.NewClient("https://demo.cachethq.io/", nil)
	if err != nil {
		panic(err)
	}

	client.Authentication.SetBasicAuth("test@test.com", "test123")

	component := &cachet.Component{
		Name: "Beer Fridge",
		Description: "Status of the beer fridge in the kitchen",
		Status: cachet.ComponentStatusOperational,
	}
	newComponent, resp, err := client.Components.Create(component)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Result: %s\n", newComponent.Name)
	if newComponent.ID > 0 {
		fmt.Println("ID > 0!")
	}
	fmt.Printf("Status: %s\n", resp.Status)

	// Output: Beer Fridge
	// ID > 0!
	// Status: 200 OK
}<commit_msg>Fixed executable examples  Please enter the commit message for your changes. Lines starting<commit_after>package cachet_test

import (
	"fmt"
	"github.com/andygrunwald/cachet"
)

func ExampleTrending_Ping() {
	client, err:= cachet.NewClient("https://demo.cachethq.io/", nil)
	if err != nil {
		panic(err)
	}

	pong, resp, err := client.General.Ping()
	if err != nil {
		panic(err)
	}

	fmt.Printf("Result: %s\n", pong)
	fmt.Printf("Status: %s\n", resp.Status)

	// Output: Result: Pong!
	// Status: 200 OK
}

func ExampleTrending_CreateComponent() {
	client, err:= cachet.NewClient("https://demo.cachethq.io/", nil)
	if err != nil {
		panic(err)
	}

	client.Authentication.SetBasicAuth("test@test.com", "test123")

	component := &cachet.Component{
		Name: "Beer Fridge",
		Description: "Status of the beer fridge in the kitchen",
		Status: cachet.ComponentStatusOperational,
	}
	newComponent, resp, err := client.Components.Create(component)
	if err != nil {
		panic(err)
	}

	fmt.Printf("Result: %s\n", newComponent.Name)
	if newComponent.ID > 0 {
		fmt.Println("ID > 0!")
	}
	fmt.Printf("Status: %s\n", resp.Status)

	// Output: Result: Beer Fridge
	// ID > 0!
	// Status: 200 OK
}<|endoftext|>