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

package walk

import (
	"fmt"
	"math/big"
	"syscall"
	"time"
	"unsafe"
)

import . "github.com/lxn/go-winapi"

type ComboBox struct {
	WidgetBase
	bindingValueProvider         BindingValueProvider
	model                        ListModel
	providedModel                interface{}
	bindingMember                string
	displayMember                string
	format                       string
	precision                    int
	itemsResetHandlerHandle      int
	itemChangedHandlerHandle     int
	maxItemTextWidth             int
	prevCurIndex                 int
	selChangeIndex               int
	currentIndexChangedPublisher EventPublisher
	editOrigWndProcPtr           uintptr
}

var comboBoxEditWndProcPtr = syscall.NewCallback(comboBoxEditWndProc)

func comboBoxEditWndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	cb := (*ComboBox)(unsafe.Pointer(GetWindowLongPtr(hwnd, GWLP_USERDATA)))

	switch msg {
	case WM_GETDLGCODE:
		if root := rootWidget(cb); root != nil {
			if dlg, ok := root.(dialogish); ok {
				if dlg.DefaultButton() != nil {
					// If the ComboBox lives in a Dialog that has a
					// DefaultButton, we won't swallow the return key.
					break
				}
			}
		}

		if wParam == VK_RETURN {
			return DLGC_WANTALLKEYS
		}

	case WM_KEYDOWN:
		if wParam != VK_RETURN || 0 == cb.SendMessage(CB_GETDROPPEDSTATE, 0, 0) {
			cb.handleKeyDown(wParam, lParam)
		}

	case WM_KEYUP:
		if wParam != VK_RETURN || 0 == cb.SendMessage(CB_GETDROPPEDSTATE, 0, 0) {
			cb.handleKeyUp(wParam, lParam)
		}
	}

	return CallWindowProc(cb.editOrigWndProcPtr, hwnd, msg, wParam, lParam)
}

func NewComboBox(parent Container) (*ComboBox, error) {
	cb, err := newComboBoxWithStyle(parent, CBS_DROPDOWN)
	if err != nil {
		return nil, err
	}

	editHwnd := GetWindow(cb.hWnd, GW_CHILD)

	SetWindowLongPtr(editHwnd, GWLP_USERDATA, uintptr(unsafe.Pointer(cb)))
	cb.editOrigWndProcPtr = SetWindowLongPtr(editHwnd, GWLP_WNDPROC, comboBoxEditWndProcPtr)

	return cb, nil
}

func NewDropDownBox(parent Container) (*ComboBox, error) {
	return newComboBoxWithStyle(parent, CBS_DROPDOWNLIST)
}

func newComboBoxWithStyle(parent Container, style uint32) (*ComboBox, error) {
	cb := &ComboBox{prevCurIndex: -1, selChangeIndex: -1, precision: 2}

	if err := InitChildWidget(
		cb,
		parent,
		"COMBOBOX",
		WS_TABSTOP|WS_VISIBLE|WS_VSCROLL|style,
		0); err != nil {
		return nil, err
	}

	cb.MustRegisterProperty("CurrentIndex", NewProperty(
		func() interface{} {
			return cb.CurrentIndex()
		},
		func(v interface{}) error {
			return cb.SetCurrentIndex(v.(int))
		},
		cb.CurrentIndexChanged()))

	cb.MustRegisterProperty("Value", NewProperty(
		func() interface{} {
			if cb.Editable() {
				return cb.Text()
			}

			index := cb.CurrentIndex()

			if cb.bindingValueProvider == nil || index == -1 {
				return nil
			}

			return cb.bindingValueProvider.BindingValue(index)
		},
		func(v interface{}) error {
			if cb.Editable() {
				return cb.SetText(v.(string))
			}

			if cb.bindingValueProvider == nil {
				return newError("Data binding is only supported using a model that implements BindingValueProvider.")
			}

			index := -1

			count := cb.model.ItemCount()
			for i := 0; i < count; i++ {
				if cb.bindingValueProvider.BindingValue(i) == v {
					index = i
					break
				}
			}

			return cb.SetCurrentIndex(index)
		},
		cb.CurrentIndexChanged()))

	return cb, nil
}

func (*ComboBox) LayoutFlags() LayoutFlags {
	return GrowableHorz
}

func (cb *ComboBox) MinSizeHint() Size {
	defaultSize := cb.dialogBaseUnitsToPixels(Size{50, 12})

	if cb.model != nil && cb.maxItemTextWidth <= 0 {
		cb.maxItemTextWidth = cb.calculateMaxItemTextWidth()
	}

	// FIXME: Use GetThemePartSize instead of guessing
	w := maxi(defaultSize.Width, cb.maxItemTextWidth+24)
	h := defaultSize.Height + 1

	return Size{w, h}
}

func (cb *ComboBox) SizeHint() Size {
	return cb.MinSizeHint()
}

func (cb *ComboBox) Editable() bool {
	return !cb.hasStyleBits(CBS_DROPDOWNLIST)
}

func (cb *ComboBox) itemString(index int) string {
	switch val := cb.model.Value(index).(type) {
	case string:
		return val

	case time.Time:
		return val.Format(cb.format)

	case *big.Rat:
		return val.FloatString(cb.precision)

	default:
		return fmt.Sprintf(cb.format, val)
	}

	panic("unreachable")
}

func (cb *ComboBox) insertItemAt(index int) error {
	str := cb.itemString(index)
	lp := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))

	if CB_ERR == cb.SendMessage(CB_INSERTSTRING, uintptr(index), lp) {
		return newError("SendMessage(CB_INSERTSTRING)")
	}

	return nil
}

func (cb *ComboBox) resetItems() error {
	cb.SetSuspended(true)
	defer cb.SetSuspended(false)

	if FALSE == cb.SendMessage(CB_RESETCONTENT, 0, 0) {
		return newError("SendMessage(CB_RESETCONTENT)")
	}

	cb.maxItemTextWidth = 0

	cb.SetCurrentIndex(-1)

	if cb.model == nil {
		return nil
	}

	count := cb.model.ItemCount()

	for i := 0; i < count; i++ {
		if err := cb.insertItemAt(i); err != nil {
			return err
		}
	}

	return nil
}

func (cb *ComboBox) attachModel() {
	itemsResetHandler := func() {
		cb.resetItems()
	}
	cb.itemsResetHandlerHandle = cb.model.ItemsReset().Attach(itemsResetHandler)

	itemChangedHandler := func(index int) {
		if CB_ERR == cb.SendMessage(CB_DELETESTRING, uintptr(index), 0) {
			newError("SendMessage(CB_DELETESTRING)")
		}

		cb.insertItemAt(index)

		cb.SetCurrentIndex(cb.prevCurIndex)
	}
	cb.itemChangedHandlerHandle = cb.model.ItemChanged().Attach(itemChangedHandler)
}

func (cb *ComboBox) detachModel() {
	cb.model.ItemsReset().Detach(cb.itemsResetHandlerHandle)
	cb.model.ItemChanged().Detach(cb.itemChangedHandlerHandle)
}

// Model returns the model of the ComboBox.
func (cb *ComboBox) Model() interface{} {
	return cb.providedModel
}

// SetModel sets the model of the ComboBox.
//
// It is required that mdl either implements walk.ListModel or
// walk.ReflectListModel or be a slice of pointers to struct or a []string.
func (cb *ComboBox) SetModel(mdl interface{}) error {
	model, ok := mdl.(ListModel)
	if !ok && mdl != nil {
		var err error
		if model, err = newReflectListModel(mdl); err != nil {
			return err
		}

		if _, ok := mdl.([]string); !ok {
			if badms, ok := model.(bindingAndDisplayMemberSetter); ok {
				var bindingMember string
				if cb.Editable() {
					bindingMember = cb.displayMember
				} else {
					bindingMember = cb.bindingMember
				}
				badms.setBindingMember(bindingMember)
				badms.setDisplayMember(cb.displayMember)
			}
		}
	}
	cb.providedModel = mdl

	if cb.model != nil {
		cb.detachModel()
	}

	cb.model = model
	cb.bindingValueProvider, _ = model.(BindingValueProvider)

	if model != nil {
		cb.attachModel()
	}

	if err := cb.resetItems(); err != nil {
		return err
	}

	if model != nil && model.ItemCount() == 1 {
		cb.SetCurrentIndex(0)
	}

	return cb.Invalidate()
}

// BindingMember returns the member from the model of the ComboBox that is bound
// to a field of the data source managed by an associated DataBinder.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
func (cb *ComboBox) BindingMember() string {
	return cb.bindingMember
}

// SetBindingMember sets the member from the model of the ComboBox that is bound
// to a field of the data source managed by an associated DataBinder.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
//
// For a model consisting of items of type S, data source field of type T and
// bindingMember "Foo", this can be one of the following:
//
//	A field		Foo T
//	A method	func (s S) Foo() T
//	A method	func (s S) Foo() (T, error)
//
// If bindingMember is not a simple member name like "Foo", but a path to a
// member like "A.B.Foo", members "A" and "B" both must be one of the options
// mentioned above, but with T having type pointer to struct.
func (cb *ComboBox) SetBindingMember(bindingMember string) error {
	if bindingMember != "" {
		if _, ok := cb.providedModel.([]string); ok {
			return newError("invalid for []string model")
		}
	}

	cb.bindingMember = bindingMember

	if badms, ok := cb.model.(bindingAndDisplayMemberSetter); ok {
		badms.setBindingMember(bindingMember)
	}

	return nil
}

// DisplayMember returns the member from the model of the ComboBox that is
// displayed in the ComboBox.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
func (cb *ComboBox) DisplayMember() string {
	return cb.displayMember
}

// SetDisplayMember sets the member from the model of the ComboBox that is
// displayed in the ComboBox.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
//
// For a model consisting of items of type S, the type of the specified member T
// and displayMember "Foo", this can be one of the following:
//
//	A field		Foo T
//	A method	func (s S) Foo() T
//	A method	func (s S) Foo() (T, error)
//
// If displayMember is not a simple member name like "Foo", but a path to a
// member like "A.B.Foo", members "A" and "B" both must be one of the options
// mentioned above, but with T having type pointer to struct.
func (cb *ComboBox) SetDisplayMember(displayMember string) error {
	if displayMember != "" {
		if _, ok := cb.providedModel.([]string); ok {
			return newError("invalid for []string model")
		}
	}

	cb.displayMember = displayMember

	if badms, ok := cb.model.(bindingAndDisplayMemberSetter); ok {
		badms.setDisplayMember(displayMember)
	}

	return nil
}

func (cb *ComboBox) Format() string {
	return cb.format
}

func (cb *ComboBox) SetFormat(value string) {
	cb.format = value
}

func (cb *ComboBox) Precision() int {
	return cb.precision
}

func (cb *ComboBox) SetPrecision(value int) {
	cb.precision = value
}

func (cb *ComboBox) calculateMaxItemTextWidth() int {
	hdc := GetDC(cb.hWnd)
	if hdc == 0 {
		newError("GetDC failed")
		return -1
	}
	defer ReleaseDC(cb.hWnd, hdc)

	hFontOld := SelectObject(hdc, HGDIOBJ(cb.Font().handleForDPI(0)))
	defer SelectObject(hdc, hFontOld)

	var maxWidth int

	count := cb.model.ItemCount()
	for i := 0; i < count; i++ {
		var s SIZE
		str := syscall.StringToUTF16(cb.itemString(i))

		if !GetTextExtentPoint32(hdc, &str[0], int32(len(str)-1), &s) {
			newError("GetTextExtentPoint32 failed")
			return -1
		}

		maxWidth = maxi(maxWidth, int(s.CX))
	}

	return maxWidth
}

func (cb *ComboBox) CurrentIndex() int {
	return int(int32(cb.SendMessage(CB_GETCURSEL, 0, 0)))
}

func (cb *ComboBox) SetCurrentIndex(value int) error {
	index := int(int32(cb.SendMessage(CB_SETCURSEL, uintptr(value), 0)))

	if index != value {
		return newError("invalid index")
	}

	if value != cb.prevCurIndex {
		cb.prevCurIndex = value
		cb.currentIndexChangedPublisher.Publish()
	}

	return nil
}

func (cb *ComboBox) CurrentIndexChanged() *Event {
	return cb.currentIndexChangedPublisher.Event()
}

func (cb *ComboBox) Text() string {
	return widgetText(cb.hWnd)
}

func (cb *ComboBox) SetText(value string) error {
	return setWidgetText(cb.hWnd, value)
}

func (cb *ComboBox) TextSelection() (start, end int) {
	cb.SendMessage(CB_GETEDITSEL, uintptr(unsafe.Pointer(&start)), uintptr(unsafe.Pointer(&end)))
	return
}

func (cb *ComboBox) SetTextSelection(start, end int) {
	cb.SendMessage(CB_SETEDITSEL, 0, uintptr(MAKELONG(uint16(start), uint16(end))))
}

func (cb *ComboBox) WndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_COMMAND:
		code := HIWORD(uint32(wParam))
		selIndex := cb.CurrentIndex()

		switch code {
		case CBN_SELCHANGE:
			cb.selChangeIndex = selIndex

		case CBN_SELENDCANCEL:
			if cb.selChangeIndex != -1 {
				cb.SetCurrentIndex(cb.selChangeIndex)

				cb.selChangeIndex = -1
			}

		case CBN_SELENDOK:
			if selIndex != cb.prevCurIndex {
				cb.currentIndexChangedPublisher.Publish()
				cb.prevCurIndex = selIndex
				return 0
			}

			cb.selChangeIndex = -1
		}
	}

	return cb.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
<commit_msg>ComboBox: Add Text property<commit_after>// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package walk

import (
	"fmt"
	"math/big"
	"syscall"
	"time"
	"unsafe"
)

import . "github.com/lxn/go-winapi"

type ComboBox struct {
	WidgetBase
	bindingValueProvider         BindingValueProvider
	model                        ListModel
	providedModel                interface{}
	bindingMember                string
	displayMember                string
	format                       string
	precision                    int
	itemsResetHandlerHandle      int
	itemChangedHandlerHandle     int
	maxItemTextWidth             int
	prevCurIndex                 int
	selChangeIndex               int
	currentIndexChangedPublisher EventPublisher
	editOrigWndProcPtr           uintptr
}

var comboBoxEditWndProcPtr = syscall.NewCallback(comboBoxEditWndProc)

func comboBoxEditWndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	cb := (*ComboBox)(unsafe.Pointer(GetWindowLongPtr(hwnd, GWLP_USERDATA)))

	switch msg {
	case WM_GETDLGCODE:
		if root := rootWidget(cb); root != nil {
			if dlg, ok := root.(dialogish); ok {
				if dlg.DefaultButton() != nil {
					// If the ComboBox lives in a Dialog that has a
					// DefaultButton, we won't swallow the return key.
					break
				}
			}
		}

		if wParam == VK_RETURN {
			return DLGC_WANTALLKEYS
		}

	case WM_KEYDOWN:
		if wParam != VK_RETURN || 0 == cb.SendMessage(CB_GETDROPPEDSTATE, 0, 0) {
			cb.handleKeyDown(wParam, lParam)
		}

	case WM_KEYUP:
		if wParam != VK_RETURN || 0 == cb.SendMessage(CB_GETDROPPEDSTATE, 0, 0) {
			cb.handleKeyUp(wParam, lParam)
		}
	}

	return CallWindowProc(cb.editOrigWndProcPtr, hwnd, msg, wParam, lParam)
}

func NewComboBox(parent Container) (*ComboBox, error) {
	cb, err := newComboBoxWithStyle(parent, CBS_DROPDOWN)
	if err != nil {
		return nil, err
	}

	editHwnd := GetWindow(cb.hWnd, GW_CHILD)

	SetWindowLongPtr(editHwnd, GWLP_USERDATA, uintptr(unsafe.Pointer(cb)))
	cb.editOrigWndProcPtr = SetWindowLongPtr(editHwnd, GWLP_WNDPROC, comboBoxEditWndProcPtr)

	return cb, nil
}

func NewDropDownBox(parent Container) (*ComboBox, error) {
	return newComboBoxWithStyle(parent, CBS_DROPDOWNLIST)
}

func newComboBoxWithStyle(parent Container, style uint32) (*ComboBox, error) {
	cb := &ComboBox{prevCurIndex: -1, selChangeIndex: -1, precision: 2}

	if err := InitChildWidget(
		cb,
		parent,
		"COMBOBOX",
		WS_TABSTOP|WS_VISIBLE|WS_VSCROLL|style,
		0); err != nil {
		return nil, err
	}

	cb.MustRegisterProperty("CurrentIndex", NewProperty(
		func() interface{} {
			return cb.CurrentIndex()
		},
		func(v interface{}) error {
			return cb.SetCurrentIndex(v.(int))
		},
		cb.CurrentIndexChanged()))

	cb.MustRegisterProperty("Text", NewProperty(
		func() interface{} {
			return cb.Text()
		},
		func(v interface{}) error {
			return cb.SetText(v.(string))
		},
		cb.CurrentIndexChanged()))

	cb.MustRegisterProperty("Value", NewProperty(
		func() interface{} {
			if cb.Editable() {
				return cb.Text()
			}

			index := cb.CurrentIndex()

			if cb.bindingValueProvider == nil || index == -1 {
				return nil
			}

			return cb.bindingValueProvider.BindingValue(index)
		},
		func(v interface{}) error {
			if cb.Editable() {
				return cb.SetText(v.(string))
			}

			if cb.bindingValueProvider == nil {
				return newError("Data binding is only supported using a model that implements BindingValueProvider.")
			}

			index := -1

			count := cb.model.ItemCount()
			for i := 0; i < count; i++ {
				if cb.bindingValueProvider.BindingValue(i) == v {
					index = i
					break
				}
			}

			return cb.SetCurrentIndex(index)
		},
		cb.CurrentIndexChanged()))

	return cb, nil
}

func (*ComboBox) LayoutFlags() LayoutFlags {
	return GrowableHorz
}

func (cb *ComboBox) MinSizeHint() Size {
	defaultSize := cb.dialogBaseUnitsToPixels(Size{50, 12})

	if cb.model != nil && cb.maxItemTextWidth <= 0 {
		cb.maxItemTextWidth = cb.calculateMaxItemTextWidth()
	}

	// FIXME: Use GetThemePartSize instead of guessing
	w := maxi(defaultSize.Width, cb.maxItemTextWidth+24)
	h := defaultSize.Height + 1

	return Size{w, h}
}

func (cb *ComboBox) SizeHint() Size {
	return cb.MinSizeHint()
}

func (cb *ComboBox) Editable() bool {
	return !cb.hasStyleBits(CBS_DROPDOWNLIST)
}

func (cb *ComboBox) itemString(index int) string {
	switch val := cb.model.Value(index).(type) {
	case string:
		return val

	case time.Time:
		return val.Format(cb.format)

	case *big.Rat:
		return val.FloatString(cb.precision)

	default:
		return fmt.Sprintf(cb.format, val)
	}

	panic("unreachable")
}

func (cb *ComboBox) insertItemAt(index int) error {
	str := cb.itemString(index)
	lp := uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(str)))

	if CB_ERR == cb.SendMessage(CB_INSERTSTRING, uintptr(index), lp) {
		return newError("SendMessage(CB_INSERTSTRING)")
	}

	return nil
}

func (cb *ComboBox) resetItems() error {
	cb.SetSuspended(true)
	defer cb.SetSuspended(false)

	if FALSE == cb.SendMessage(CB_RESETCONTENT, 0, 0) {
		return newError("SendMessage(CB_RESETCONTENT)")
	}

	cb.maxItemTextWidth = 0

	cb.SetCurrentIndex(-1)

	if cb.model == nil {
		return nil
	}

	count := cb.model.ItemCount()

	for i := 0; i < count; i++ {
		if err := cb.insertItemAt(i); err != nil {
			return err
		}
	}

	return nil
}

func (cb *ComboBox) attachModel() {
	itemsResetHandler := func() {
		cb.resetItems()
	}
	cb.itemsResetHandlerHandle = cb.model.ItemsReset().Attach(itemsResetHandler)

	itemChangedHandler := func(index int) {
		if CB_ERR == cb.SendMessage(CB_DELETESTRING, uintptr(index), 0) {
			newError("SendMessage(CB_DELETESTRING)")
		}

		cb.insertItemAt(index)

		cb.SetCurrentIndex(cb.prevCurIndex)
	}
	cb.itemChangedHandlerHandle = cb.model.ItemChanged().Attach(itemChangedHandler)
}

func (cb *ComboBox) detachModel() {
	cb.model.ItemsReset().Detach(cb.itemsResetHandlerHandle)
	cb.model.ItemChanged().Detach(cb.itemChangedHandlerHandle)
}

// Model returns the model of the ComboBox.
func (cb *ComboBox) Model() interface{} {
	return cb.providedModel
}

// SetModel sets the model of the ComboBox.
//
// It is required that mdl either implements walk.ListModel or
// walk.ReflectListModel or be a slice of pointers to struct or a []string.
func (cb *ComboBox) SetModel(mdl interface{}) error {
	model, ok := mdl.(ListModel)
	if !ok && mdl != nil {
		var err error
		if model, err = newReflectListModel(mdl); err != nil {
			return err
		}

		if _, ok := mdl.([]string); !ok {
			if badms, ok := model.(bindingAndDisplayMemberSetter); ok {
				var bindingMember string
				if cb.Editable() {
					bindingMember = cb.displayMember
				} else {
					bindingMember = cb.bindingMember
				}
				badms.setBindingMember(bindingMember)
				badms.setDisplayMember(cb.displayMember)
			}
		}
	}
	cb.providedModel = mdl

	if cb.model != nil {
		cb.detachModel()
	}

	cb.model = model
	cb.bindingValueProvider, _ = model.(BindingValueProvider)

	if model != nil {
		cb.attachModel()
	}

	if err := cb.resetItems(); err != nil {
		return err
	}

	if model != nil && model.ItemCount() == 1 {
		cb.SetCurrentIndex(0)
	}

	return cb.Invalidate()
}

// BindingMember returns the member from the model of the ComboBox that is bound
// to a field of the data source managed by an associated DataBinder.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
func (cb *ComboBox) BindingMember() string {
	return cb.bindingMember
}

// SetBindingMember sets the member from the model of the ComboBox that is bound
// to a field of the data source managed by an associated DataBinder.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
//
// For a model consisting of items of type S, data source field of type T and
// bindingMember "Foo", this can be one of the following:
//
//	A field		Foo T
//	A method	func (s S) Foo() T
//	A method	func (s S) Foo() (T, error)
//
// If bindingMember is not a simple member name like "Foo", but a path to a
// member like "A.B.Foo", members "A" and "B" both must be one of the options
// mentioned above, but with T having type pointer to struct.
func (cb *ComboBox) SetBindingMember(bindingMember string) error {
	if bindingMember != "" {
		if _, ok := cb.providedModel.([]string); ok {
			return newError("invalid for []string model")
		}
	}

	cb.bindingMember = bindingMember

	if badms, ok := cb.model.(bindingAndDisplayMemberSetter); ok {
		badms.setBindingMember(bindingMember)
	}

	return nil
}

// DisplayMember returns the member from the model of the ComboBox that is
// displayed in the ComboBox.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
func (cb *ComboBox) DisplayMember() string {
	return cb.displayMember
}

// SetDisplayMember sets the member from the model of the ComboBox that is
// displayed in the ComboBox.
//
// This is only applicable to walk.ReflectListModel models and simple slices of
// pointers to struct.
//
// For a model consisting of items of type S, the type of the specified member T
// and displayMember "Foo", this can be one of the following:
//
//	A field		Foo T
//	A method	func (s S) Foo() T
//	A method	func (s S) Foo() (T, error)
//
// If displayMember is not a simple member name like "Foo", but a path to a
// member like "A.B.Foo", members "A" and "B" both must be one of the options
// mentioned above, but with T having type pointer to struct.
func (cb *ComboBox) SetDisplayMember(displayMember string) error {
	if displayMember != "" {
		if _, ok := cb.providedModel.([]string); ok {
			return newError("invalid for []string model")
		}
	}

	cb.displayMember = displayMember

	if badms, ok := cb.model.(bindingAndDisplayMemberSetter); ok {
		badms.setDisplayMember(displayMember)
	}

	return nil
}

func (cb *ComboBox) Format() string {
	return cb.format
}

func (cb *ComboBox) SetFormat(value string) {
	cb.format = value
}

func (cb *ComboBox) Precision() int {
	return cb.precision
}

func (cb *ComboBox) SetPrecision(value int) {
	cb.precision = value
}

func (cb *ComboBox) calculateMaxItemTextWidth() int {
	hdc := GetDC(cb.hWnd)
	if hdc == 0 {
		newError("GetDC failed")
		return -1
	}
	defer ReleaseDC(cb.hWnd, hdc)

	hFontOld := SelectObject(hdc, HGDIOBJ(cb.Font().handleForDPI(0)))
	defer SelectObject(hdc, hFontOld)

	var maxWidth int

	count := cb.model.ItemCount()
	for i := 0; i < count; i++ {
		var s SIZE
		str := syscall.StringToUTF16(cb.itemString(i))

		if !GetTextExtentPoint32(hdc, &str[0], int32(len(str)-1), &s) {
			newError("GetTextExtentPoint32 failed")
			return -1
		}

		maxWidth = maxi(maxWidth, int(s.CX))
	}

	return maxWidth
}

func (cb *ComboBox) CurrentIndex() int {
	return int(int32(cb.SendMessage(CB_GETCURSEL, 0, 0)))
}

func (cb *ComboBox) SetCurrentIndex(value int) error {
	index := int(int32(cb.SendMessage(CB_SETCURSEL, uintptr(value), 0)))

	if index != value {
		return newError("invalid index")
	}

	if value != cb.prevCurIndex {
		cb.prevCurIndex = value
		cb.currentIndexChangedPublisher.Publish()
	}

	return nil
}

func (cb *ComboBox) CurrentIndexChanged() *Event {
	return cb.currentIndexChangedPublisher.Event()
}

func (cb *ComboBox) Text() string {
	return widgetText(cb.hWnd)
}

func (cb *ComboBox) SetText(value string) error {
	return setWidgetText(cb.hWnd, value)
}

func (cb *ComboBox) TextSelection() (start, end int) {
	cb.SendMessage(CB_GETEDITSEL, uintptr(unsafe.Pointer(&start)), uintptr(unsafe.Pointer(&end)))
	return
}

func (cb *ComboBox) SetTextSelection(start, end int) {
	cb.SendMessage(CB_SETEDITSEL, 0, uintptr(MAKELONG(uint16(start), uint16(end))))
}

func (cb *ComboBox) WndProc(hwnd HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case WM_COMMAND:
		code := HIWORD(uint32(wParam))
		selIndex := cb.CurrentIndex()

		switch code {
		case CBN_SELCHANGE:
			cb.selChangeIndex = selIndex

		case CBN_SELENDCANCEL:
			if cb.selChangeIndex != -1 {
				cb.SetCurrentIndex(cb.selChangeIndex)

				cb.selChangeIndex = -1
			}

		case CBN_SELENDOK:
			if selIndex != cb.prevCurIndex {
				cb.currentIndexChangedPublisher.Publish()
				cb.prevCurIndex = selIndex
				return 0
			}

			cb.selChangeIndex = -1
		}
	}

	return cb.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"text/tabwriter"
	"time"

	"go/ast"
	"go/build"
	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/go/types"
)

type command struct {
	name     string
	action   func(*Session, string) error
	complete func(*Session, string) []string
	arg      string
	document string
}

// TODO
// - :edit
// - :undo
// - :reset
// - :type
var commands []command

func init() {
	commands = []command{
		{
			name:     "import",
			action:   actionImport,
			complete: completeImport,
			arg:      "<package>",
			document: "import a package",
		},
		{
			name:     "print",
			action:   actionPrint,
			document: "print current source",
		},
		{
			name:     "write",
			action:   actionWrite,
			complete: nil, // TODO implement
			arg:      "[<file>]",
			document: "write out current source",
		},
		{
			name:     "doc",
			action:   actionDoc,
			complete: completeDoc,
			arg:      "<expr or pkg>",
			document: "show documentation",
		},
		{
			name:     "help",
			action:   actionHelp,
			document: "show this help",
		},
	}
}

func actionImport(s *Session, arg string) error {
	if arg == "" {
		return fmt.Errorf("arg required")
	}

	path := strings.Trim(arg, `"`)

	// check if the package specified by path is importable
	_, err := types.DefaultImport(s.Types.Packages, path)
	if err != nil {
		return err
	}

	astutil.AddImport(s.Fset, s.File, path)

	return nil
}

var gorootSrc = filepath.Join(filepath.Clean(runtime.GOROOT()), "src")

func completeImport(s *Session, prefix string) []string {
	result := []string{}
	seen := map[string]bool{}

	d, fn := path.Split(prefix)
	for _, srcDir := range build.Default.SrcDirs() {
		dir := filepath.Join(srcDir, d)

		if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
			if err != nil && !os.IsNotExist(err) {
				errorf("Stat %s: %s", dir, err)
			}
			continue
		}

		entries, err := ioutil.ReadDir(dir)
		if err != nil {
			errorf("ReadDir %s: %s", dir, err)
			continue
		}
		for _, fi := range entries {
			if !fi.IsDir() {
				continue
			}

			name := fi.Name()
			if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") || name == "testdata" {
				continue
			}

			if strings.HasPrefix(name, fn) {
				r := path.Join(d, name)
				if srcDir != gorootSrc {
					// append "/" if this directory is not a repository
					// e.g. does not have VCS directory such as .git or .hg
					// TODO: do not append "/" to subdirectories of repos
					var isRepo bool
					for _, vcsDir := range []string{".git", ".hg", ".svn", ".bzr"} {
						_, err := os.Stat(filepath.Join(srcDir, filepath.FromSlash(r), vcsDir))
						if err == nil {
							isRepo = true
							break
						}
					}
					if !isRepo {
						r = r + "/"
					}
				}

				if !seen[r] {
					result = append(result, r)
					seen[r] = true
				}
			}
		}
	}

	return result
}

func completeDoc(s *Session, prefix string) []string {
	pos, cands, err := s.completeCode(prefix, len(prefix), false)
	if err != nil {
		errorf("completeCode: %s", err)
		return nil
	}

	result := make([]string, 0, len(cands))
	for _, c := range cands {
		result = append(result, prefix[0:pos]+c)
	}

	return result
}

func actionPrint(s *Session, _ string) error {
	source, err := s.source(true)
	if err != nil {
		return err
	}

	fmt.Println(source)

	return nil
}

func actionWrite(s *Session, filename string) error {
	source, err := s.source(false)
	if err != nil {
		return err
	}

	if filename == "" {
		filename = fmt.Sprintf("gore_session_%s.go", time.Now().Format("20060102_150405"))
	}

	err = ioutil.WriteFile(filename, []byte(source), 0644)
	if err != nil {
		return err
	}

	infof("Source wrote to %s", filename)

	return nil
}

func actionDoc(s *Session, in string) error {
	s.storeMainBody()
	defer s.restoreMainBody()

	expr, err := s.evalExpr(in)
	if err != nil {
		return err
	}

	s.doQuickFix()

	s.TypeInfo = types.Info{
		Types:  make(map[ast.Expr]types.TypeAndValue),
		Uses:   make(map[*ast.Ident]types.Object),
		Defs:   make(map[*ast.Ident]types.Object),
		Scopes: make(map[ast.Node]*types.Scope),
	}
	_, err = s.Types.Check("_tmp", s.Fset, []*ast.File{s.File}, &s.TypeInfo)
	if err != nil {
		debugf("typecheck error (ignored): %s", err)
	}

	// :doc patterns:
	// - "json" -> "encoding/json" (package name)
	// - "json.Encoder" -> "encoding/json", "Encoder" (package member)
	// - "json.NewEncoder(nil).Encode" -> "encoding/json", "Decode" (package type member)
	var docObj types.Object
	if sel, ok := expr.(*ast.SelectorExpr); ok {
		// package member, package type member
		docObj = s.TypeInfo.ObjectOf(sel.Sel)
	} else if t := s.TypeInfo.TypeOf(expr); t != nil && t != types.Typ[types.Invalid] {
		for {
			if pt, ok := t.(*types.Pointer); ok {
				t = pt.Elem()
			} else {
				break
			}
		}
		switch t := t.(type) {
		case *types.Named:
			docObj = t.Obj()
		case *types.Basic:
			// builtin types
			docObj = types.Universe.Lookup(t.Name())
		}
	} else if ident, ok := expr.(*ast.Ident); ok {
		// package name
		mainScope := s.TypeInfo.Scopes[s.mainFunc().Type]
		_, docObj = mainScope.LookupParent(ident.Name)
	}

	if docObj == nil {
		return fmt.Errorf("cannot determine the document location")
	}

	debugf("doc :: obj=%#v", docObj)

	var pkgPath, objName string
	if pkgName, ok := docObj.(*types.PkgName); ok {
		pkgPath = pkgName.Imported().Path()
	} else {
		if pkg := docObj.Pkg(); pkg != nil {
			pkgPath = pkg.Path()
		} else {
			pkgPath = "builtin"
		}
		objName = docObj.Name()
	}

	debugf("doc :: %q %q", pkgPath, objName)

	args := []string{pkgPath}
	if objName != "" {
		args = append(args, objName)
	}

	godoc := exec.Command("godoc", args...)
	godoc.Stderr = os.Stderr

	// TODO just use PAGER?
	if pagerCmd := os.Getenv("GORE_PAGER"); pagerCmd != "" {
		r, err := godoc.StdoutPipe()
		if err != nil {
			return err
		}

		pager := exec.Command(pagerCmd)
		pager.Stdin = r
		pager.Stdout = os.Stdout
		pager.Stderr = os.Stderr

		err = pager.Start()
		if err != nil {
			return err
		}

		err = godoc.Run()
		if err != nil {
			return err
		}

		return pager.Wait()
	} else {
		godoc.Stdout = os.Stdout
		return godoc.Run()
	}
}

func actionHelp(s *Session, _ string) error {
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 4, ' ', 0)
	for _, command := range commands {
		cmd := ":" + command.name
		if command.arg != "" {
			cmd = cmd + " " + command.arg
		}
		w.Write([]byte("    " + cmd + "\t" + command.document + "\n"))
	}
	w.Flush()

	return nil
}
<commit_msg>Fix for newer interface<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"text/tabwriter"
	"time"

	"go/ast"
	"go/build"
	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/go/types"
)

type command struct {
	name     string
	action   func(*Session, string) error
	complete func(*Session, string) []string
	arg      string
	document string
}

// TODO
// - :edit
// - :undo
// - :reset
// - :type
var commands []command

func init() {
	commands = []command{
		{
			name:     "import",
			action:   actionImport,
			complete: completeImport,
			arg:      "<package>",
			document: "import a package",
		},
		{
			name:     "print",
			action:   actionPrint,
			document: "print current source",
		},
		{
			name:     "write",
			action:   actionWrite,
			complete: nil, // TODO implement
			arg:      "[<file>]",
			document: "write out current source",
		},
		{
			name:     "doc",
			action:   actionDoc,
			complete: completeDoc,
			arg:      "<expr or pkg>",
			document: "show documentation",
		},
		{
			name:     "help",
			action:   actionHelp,
			document: "show this help",
		},
	}
}

func actionImport(s *Session, arg string) error {
	if arg == "" {
		return fmt.Errorf("arg required")
	}

	path := strings.Trim(arg, `"`)

	// check if the package specified by path is importable
	_, err := types.DefaultImport(s.Types.Packages, path)
	if err != nil {
		return err
	}

	astutil.AddImport(s.Fset, s.File, path)

	return nil
}

var gorootSrc = filepath.Join(filepath.Clean(runtime.GOROOT()), "src")

func completeImport(s *Session, prefix string) []string {
	result := []string{}
	seen := map[string]bool{}

	d, fn := path.Split(prefix)
	for _, srcDir := range build.Default.SrcDirs() {
		dir := filepath.Join(srcDir, d)

		if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
			if err != nil && !os.IsNotExist(err) {
				errorf("Stat %s: %s", dir, err)
			}
			continue
		}

		entries, err := ioutil.ReadDir(dir)
		if err != nil {
			errorf("ReadDir %s: %s", dir, err)
			continue
		}
		for _, fi := range entries {
			if !fi.IsDir() {
				continue
			}

			name := fi.Name()
			if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") || name == "testdata" {
				continue
			}

			if strings.HasPrefix(name, fn) {
				r := path.Join(d, name)
				if srcDir != gorootSrc {
					// append "/" if this directory is not a repository
					// e.g. does not have VCS directory such as .git or .hg
					// TODO: do not append "/" to subdirectories of repos
					var isRepo bool
					for _, vcsDir := range []string{".git", ".hg", ".svn", ".bzr"} {
						_, err := os.Stat(filepath.Join(srcDir, filepath.FromSlash(r), vcsDir))
						if err == nil {
							isRepo = true
							break
						}
					}
					if !isRepo {
						r = r + "/"
					}
				}

				if !seen[r] {
					result = append(result, r)
					seen[r] = true
				}
			}
		}
	}

	return result
}

func completeDoc(s *Session, prefix string) []string {
	pos, cands, err := s.completeCode(prefix, len(prefix), false)
	if err != nil {
		errorf("completeCode: %s", err)
		return nil
	}

	result := make([]string, 0, len(cands))
	for _, c := range cands {
		result = append(result, prefix[0:pos]+c)
	}

	return result
}

func actionPrint(s *Session, _ string) error {
	source, err := s.source(true)
	if err != nil {
		return err
	}

	fmt.Println(source)

	return nil
}

func actionWrite(s *Session, filename string) error {
	source, err := s.source(false)
	if err != nil {
		return err
	}

	if filename == "" {
		filename = fmt.Sprintf("gore_session_%s.go", time.Now().Format("20060102_150405"))
	}

	err = ioutil.WriteFile(filename, []byte(source), 0644)
	if err != nil {
		return err
	}

	infof("Source wrote to %s", filename)

	return nil
}

func actionDoc(s *Session, in string) error {
	s.storeMainBody()
	defer s.restoreMainBody()

	expr, err := s.evalExpr(in)
	if err != nil {
		return err
	}

	s.doQuickFix()

	s.TypeInfo = types.Info{
		Types:  make(map[ast.Expr]types.TypeAndValue),
		Uses:   make(map[*ast.Ident]types.Object),
		Defs:   make(map[*ast.Ident]types.Object),
		Scopes: make(map[ast.Node]*types.Scope),
	}
	_, err = s.Types.Check("_tmp", s.Fset, []*ast.File{s.File}, &s.TypeInfo)
	if err != nil {
		debugf("typecheck error (ignored): %s", err)
	}

	// :doc patterns:
	// - "json" -> "encoding/json" (package name)
	// - "json.Encoder" -> "encoding/json", "Encoder" (package member)
	// - "json.NewEncoder(nil).Encode" -> "encoding/json", "Decode" (package type member)
	var docObj types.Object
	if sel, ok := expr.(*ast.SelectorExpr); ok {
		// package member, package type member
		docObj = s.TypeInfo.ObjectOf(sel.Sel)
	} else if t := s.TypeInfo.TypeOf(expr); t != nil && t != types.Typ[types.Invalid] {
		for {
			if pt, ok := t.(*types.Pointer); ok {
				t = pt.Elem()
			} else {
				break
			}
		}
		switch t := t.(type) {
		case *types.Named:
			docObj = t.Obj()
		case *types.Basic:
			// builtin types
			docObj = types.Universe.Lookup(t.Name())
		}
	} else if ident, ok := expr.(*ast.Ident); ok {
		// package name
		mainScope := s.TypeInfo.Scopes[s.mainFunc().Type]
		_, docObj = mainScope.LookupParent(ident.Name, ident.NamePos)
	}

	if docObj == nil {
		return fmt.Errorf("cannot determine the document location")
	}

	debugf("doc :: obj=%#v", docObj)

	var pkgPath, objName string
	if pkgName, ok := docObj.(*types.PkgName); ok {
		pkgPath = pkgName.Imported().Path()
	} else {
		if pkg := docObj.Pkg(); pkg != nil {
			pkgPath = pkg.Path()
		} else {
			pkgPath = "builtin"
		}
		objName = docObj.Name()
	}

	debugf("doc :: %q %q", pkgPath, objName)

	args := []string{pkgPath}
	if objName != "" {
		args = append(args, objName)
	}

	godoc := exec.Command("godoc", args...)
	godoc.Stderr = os.Stderr

	// TODO just use PAGER?
	if pagerCmd := os.Getenv("GORE_PAGER"); pagerCmd != "" {
		r, err := godoc.StdoutPipe()
		if err != nil {
			return err
		}

		pager := exec.Command(pagerCmd)
		pager.Stdin = r
		pager.Stdout = os.Stdout
		pager.Stderr = os.Stderr

		err = pager.Start()
		if err != nil {
			return err
		}

		err = godoc.Run()
		if err != nil {
			return err
		}

		return pager.Wait()
	} else {
		godoc.Stdout = os.Stdout
		return godoc.Run()
	}
}

func actionHelp(s *Session, _ string) error {
	w := tabwriter.NewWriter(os.Stdout, 0, 8, 4, ' ', 0)
	for _, command := range commands {
		cmd := ":" + command.name
		if command.arg != "" {
			cmd = cmd + " " + command.arg
		}
		w.Write([]byte("    " + cmd + "\t" + command.document + "\n"))
	}
	w.Flush()

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/serbe/ncp"
)

var (
	urls = []string{
		"/forum/viewforum.php?f=218", // Зарубежные Новинки (HD*Rip/LQ, DVDRip)
		"/forum/viewforum.php?f=221", // Отечественные Фильмы (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=225", // Зарубежные Фильмы (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=230", // Отечественные Мультфильмы (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=231", // Зарубежные Мультфильмы (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=270", // Отечественные Новинки (HD*Rip/LQ, DVDRip)
		"/forum/viewforum.php?f=319", // Зарубежная Классика (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=320", // Отечественная Классика (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
	}
)

func (a *App) get() error {
	var (
		err error
		i   int
	)
	for _, parseurl := range urls {
		topics, err := a.net.ParseForumTree(parseurl)
		if err != nil {
			log.Println("ParseForumTree ", parseurl, err)
			return err
		}
		for _, topic := range topics {
			_, err := a.getTorrentByHref(topic.Href)
			if err != nil {
				film, err := a.net.ParseTopic(topic)
				if err == nil {
					i++
					film = a.checkName(film)
					_, err = a.createTorrent(film)
					if err != nil {
						log.Println("createTorrent ", err)
					}
				} else {
					log.Println("ParseTopic ", err)
				}
			}
		}
	}
	if i > 0 {
		log.Println("Adding", i, "new films")
	} else {
		log.Println("No adding new films")
	}
	return err
}

func (a *App) update() error {
	var (
		i        int
		err      error
		torrents []Torrent
	)

	torrents, err = a.getWithDownload()
	if err != nil {
		return err
	}
	for _, tor := range torrents {
		var topic ncp.Topic
		topic.Href = tor.Href
		f, err := a.net.ParseTopic(topic)
		if err == nil {
			if f.NNM != tor.NNM || f.Seeders != tor.Seeders || f.Leechers != tor.Leechers || f.Torrent != tor.Torrent {
				i++
				a.updateTorrent(tor.ID, f)
			}
		} else {
			return err
		}
	}
	if i > 0 {
		log.Println("Update", i, "movies")
	} else {
		log.Println("No movies update")
	}
	return nil
}

func (a *App) name() error {
	var i int
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Name == strings.ToUpper(movie.Name) {
			lowerName, err := a.getUpperName(movie)
			if err == nil {
				i++
				a.updateName(movie.ID, lowerName)
			}
		}
	}
	if i > 0 {
		log.Println(i, "name fixed")
	} else {
		log.Println("No fixed names")
	}
	return nil
}

func (a *App) rating() error {
	var (
		i int
	)
	movies, err := a.getNoRating()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Kinopoisk == 0 || movie.IMDb == 0 || movie.Duration == "" {
			kp, err := a.getRating(movie)
			if err == nil {
				i++
				_ = a.updateRating(movie, kp)
			}
		}
	}
	if i > 0 {
		log.Println(i, "ratings update")
	} else {
		log.Println("No update ratings")
	}
	return nil
}

func (a *App) poster() error {
	var (
		i     int
		files []string
	)
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	filesInDir, _ := ioutil.ReadDir(a.hd)
	for _, file := range filesInDir {
		files = append(files, file.Name())
	}
	for _, movie := range movies {
		if movie.PosterURL != "" {
			if movie.Poster != "" {
				if !existsFile(a.hd + movie.Poster) {
					poster, err := a.getPoster(movie.PosterURL)
					if err == nil {
						i++
						err = a.updatePoster(movie, poster)
						if err != nil {
							log.Println("updatePoster ", poster, err)
						}
					} else {
						log.Println("getPoster ", poster, err)
					}
				} else {
					files = deleteFromSlice(files, movie.Poster)
				}
			} else {
				poster, err := a.getPoster(movie.PosterURL)
				if err == nil {
					i++
					_ = a.updatePoster(movie, poster)
				}
			}
		} else {
			tor, err := a.getTorrentByMovieID(movie.ID)
			if err == nil {
				var topic ncp.Topic
				topic.Href = tor.Href
				tempFilm, err := a.net.ParseTopic(topic)
				if err == nil {
					if tempFilm.Poster != "" {
						err = a.updatePosterURL(movie, tempFilm.Poster)
						if err == nil {
							poster, err := a.getPoster(tempFilm.Poster)
							if err == nil {
								i++
								_ = a.updatePoster(movie, poster)
							}
						}
					}
				}
			}
		}
	}
	for _, file := range files {
		_ = os.Remove(a.hd + file)
	}
	if i > 0 {
		log.Println(i, "posters update")
	} else {
		log.Println("No update posters")
	}
	if len(files) > 0 {
		log.Println("Remove", len(files), "unused images")
	}
	return nil
}
<commit_msg>no save nonparsed topics<commit_after>package main

import (
	"io/ioutil"
	"log"
	"os"
	"strings"

	"github.com/serbe/ncp"
)

var (
	urls = []string{
		"/forum/viewforum.php?f=218", // Зарубежные Новинки (HD*Rip/LQ, DVDRip)
		"/forum/viewforum.php?f=221", // Отечественные Фильмы (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=225", // Зарубежные Фильмы (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=230", // Отечественные Мультфильмы (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=231", // Зарубежные Мультфильмы (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=270", // Отечественные Новинки (HD*Rip/LQ, DVDRip)
		"/forum/viewforum.php?f=319", // Зарубежная Классика (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
		"/forum/viewforum.php?f=320", // Отечественная Классика (HD*Rip/LQ, DVDRip, SATRip, VHSRip)
	}
)

func (a *App) get() error {
	var (
		err error
		i   int
	)
	for _, parseurl := range urls {
		topics, err := a.net.ParseForumTree(parseurl)
		if err != nil {
			log.Println("ParseForumTree ", parseurl, err)
			return err
		}
		for _, topic := range topics {
			_, err := a.getTorrentByHref(topic.Href)
			if err != nil {
				film, err := a.net.ParseTopic(topic)
				if err == nil && film.Description == "" {
					i++
					film = a.checkName(film)
					_, err = a.createTorrent(film)
					if err != nil {
						log.Println("createTorrent ", err)
					}
				} else {
					log.Println("ParseTopic ", err)
				}
			}
		}
	}
	if i > 0 {
		log.Println("Adding", i, "new films")
	} else {
		log.Println("No adding new films")
	}
	return err
}

func (a *App) update() error {
	var (
		i        int
		err      error
		torrents []Torrent
	)

	torrents, err = a.getWithDownload()
	if err != nil {
		return err
	}
	for _, tor := range torrents {
		var topic ncp.Topic
		topic.Href = tor.Href
		f, err := a.net.ParseTopic(topic)
		if err == nil {
			if f.NNM != tor.NNM || f.Seeders != tor.Seeders || f.Leechers != tor.Leechers || f.Torrent != tor.Torrent {
				i++
				a.updateTorrent(tor.ID, f)
			}
		} else {
			return err
		}
	}
	if i > 0 {
		log.Println("Update", i, "movies")
	} else {
		log.Println("No movies update")
	}
	return nil
}

func (a *App) name() error {
	var i int
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Name == strings.ToUpper(movie.Name) {
			lowerName, err := a.getUpperName(movie)
			if err == nil {
				i++
				a.updateName(movie.ID, lowerName)
			}
		}
	}
	if i > 0 {
		log.Println(i, "name fixed")
	} else {
		log.Println("No fixed names")
	}
	return nil
}

func (a *App) rating() error {
	var (
		i int
	)
	movies, err := a.getNoRating()
	if err != nil {
		return err
	}
	for _, movie := range movies {
		if movie.Kinopoisk == 0 || movie.IMDb == 0 || movie.Duration == "" {
			kp, err := a.getRating(movie)
			if err == nil {
				i++
				_ = a.updateRating(movie, kp)
			}
		}
	}
	if i > 0 {
		log.Println(i, "ratings update")
	} else {
		log.Println("No update ratings")
	}
	return nil
}

func (a *App) poster() error {
	var (
		i     int
		files []string
	)
	movies, err := a.getMovies()
	if err != nil {
		return err
	}
	filesInDir, _ := ioutil.ReadDir(a.hd)
	for _, file := range filesInDir {
		files = append(files, file.Name())
	}
	for _, movie := range movies {
		if movie.PosterURL != "" {
			if movie.Poster != "" {
				if !existsFile(a.hd + movie.Poster) {
					poster, err := a.getPoster(movie.PosterURL)
					if err == nil {
						i++
						err = a.updatePoster(movie, poster)
						if err != nil {
							log.Println("updatePoster ", poster, err)
						}
					} else {
						log.Println("getPoster ", poster, err)
					}
				} else {
					files = deleteFromSlice(files, movie.Poster)
				}
			} else {
				poster, err := a.getPoster(movie.PosterURL)
				if err == nil {
					i++
					_ = a.updatePoster(movie, poster)
				}
			}
		} else {
			tor, err := a.getTorrentByMovieID(movie.ID)
			if err == nil {
				var topic ncp.Topic
				topic.Href = tor.Href
				tempFilm, err := a.net.ParseTopic(topic)
				if err == nil {
					if tempFilm.Poster != "" {
						err = a.updatePosterURL(movie, tempFilm.Poster)
						if err == nil {
							poster, err := a.getPoster(tempFilm.Poster)
							if err == nil {
								i++
								_ = a.updatePoster(movie, poster)
							}
						}
					}
				}
			}
		}
	}
	for _, file := range files {
		_ = os.Remove(a.hd + file)
	}
	if i > 0 {
		log.Println(i, "posters update")
	} else {
		log.Println("No update posters")
	}
	if len(files) > 0 {
		log.Println("Remove", len(files), "unused images")
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/bwmarrin/discordgo"
	"io"
	"os"
	"strconv"
)

// generic command struct which contains name, description, and a function
type command struct {
	name             string                                                                                // human-readable name of the command
	description      string                                                                                // description of command's function
	usage            string                                                                                // example of how to correctly use command - [] for optional arguments, <> for required arguments
	verbs            []string                                                                               // all verbs which are mapped to the same command
	requiresDatabase bool                                                                                // does this command require database access?
	function         func([]string, *discordgo.Channel, *discordgo.User, *discordgo.Session) *commandOutput // function which receives a slice of arguments and returns a string to display to the user
}

// output returned by all command functions, can contain a file to be uploaded
type commandOutput struct {
	response string
	file     io.Reader
	embed    *discordgo.MessageEmbed
}

func initCommands() map[string]*command {
	commandList := []*command{}

	commandList = append(commandList,

		// Define all commands here in the order they will be displayed by the help command
		// The 'usage' field should use the default verb
		// Do not include the command prefix

		&command{
			name:             "Display help",
			description:      "Lists all commands and their purposes.\nCan also display detailed info about a given command.",
			usage:            "help [verb]",
			verbs:            []string{"help", "commands"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, user *discordgo.User, discordSession *discordgo.Session) *commandOutput {

				DebugPrint("Running help command.")

				if len(args) <= 0 {

					DebugPrint("No arguments; listing commands.")

					embed := NewEmbed().
						SetTitle("Source").
						SetAuthor("Sunbot " + version).
						SetDescription("Database enabled: " + strconv.FormatBool(redisEnabled)).
						SetURL("https://github.com/techniponi/sunbot").
						SetImage(discordSession.State.User.AvatarURL("128"))

					for _, cmd := range commandList {
						if cmd.requiresDatabase && !redisEnabled {
							// Database is not enabled, this command needs it
						} else {
							embed.AddField(cmd.name, "`"+cfg.DefaultPrefix+cmd.usage+"`")
						}
					}

					return &commandOutput{embed: embed.MessageEmbed}
				} else {

					DebugPrint("Verb was given...")

					// check if command exists
					if cmd, ok := commands[args[0]]; ok {

						embed := NewEmbed().
							SetTitle(cmd.name).
							SetDescription(cmd.description).
							AddField("Usage", "`"+cfg.DefaultPrefix+cmd.usage+"`")

						DebugPrint("Providing help for given verb.")

						// compile verbs
						verbOutput := ""
						for index, verb := range cmd.verbs {
							// don't add a comma if it's the last one
							if index == (len(cmd.verbs) - 1) {
								verbOutput += "`" + cfg.DefaultPrefix + verb + "`"
							} else {
								verbOutput += "`" + cfg.DefaultPrefix + verb + "`, "
							}
						}

						embed.AddField("Verbs", verbOutput)

						return &commandOutput{embed: embed.MessageEmbed}
					} else {
						DebugPrint("Given verb was not found.")
						return &commandOutput{response: "That isn't a valid command."}
					}
				}
			},
		},

		&command{
			name:             "Derpibooru search",
			description:      "Searches Derpibooru with the given tags as the query, chooses a random result to display.\nUse commas to separate tags like you would on the website.",
			usage:            "derpi <tags>",
			verbs:            []string{"derpi", "db", "derpibooru"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, user *discordgo.User, discordSession *discordgo.Session) *commandOutput {
				if len(args) < 1 {
					DebugPrint("User ran derpibooru command with no tags given.")
					return &commandOutput{response: "Error: no tags specified"}
				} else {
					DebugPrint("User is running derpibooru command...")

					searchQuery := ""

					for _, arg := range args {
						searchQuery += arg + " "
					}

					// enforce 'safe' tag if channel is not nsfw
					if !channel.NSFW {
						DebugPrint("Channel #" + channel.Name + " is SFW, adding safe tag...")
						searchQuery += ",safe"
					}

					DebugPrint("Searching with tags:\n" + searchQuery)

					// use derpibooru.go to perform search
					results, err := DerpiSearchWithTags(searchQuery, cfg.DerpiApiKey)
					if err != nil {
						fmt.Println(err)
						return &commandOutput{response: "Error: " + err.Error()}
					}

					// check for results
					if len(results.Search) <= 0 {
						DebugPrint("Derpibooru returned no results.")
						return &commandOutput{response: "Error: no results."}
					} else {
						DebugPrint("Derpibooru returned results; parsed successfully.")
						// pick one randomly
						output := "http:" + results.Search[RandomRange(0, len(results.Search))].Image

						return &commandOutput{response: output}
					}
				}
			},
		},

		&command{
			name:             "Gay",
			description:      "Posts a very gay image.",
			usage:            "gay",
			verbs:            []string{"gay"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, user *discordgo.User, discordSession *discordgo.Session) *commandOutput {
				file, err := os.Open("img/gaybats.png") // TODO: move this to database; allow users to add images (permission system?)
				if err != nil {
					return &commandOutput{response: "Error opening file"}

				}
				return &commandOutput{file: file}
			},
		},

		&command{
			name:             "stats",
			description:      "Displays the statistics of the user.",
			usage:            "stats [user]", // TODO: implement pinging users
			verbs:            []string{"stats"},
			requiresDatabase: true,
			function: func(args []string, channel *discordgo.Channel, user *discordgo.User, discordSession *discordgo.Session) *commandOutput {
				userDb, err := GetUser(user, false)
				if err != nil {
					return &commandOutput{response: "That user doesn't exist in the database yet. They need to chat some!"}
				}

				posts := userDb.Val()["posts"]
				return &commandOutput{response: "You have made " + posts + " posts! (THIS COMMAND IS WIP)"} // TODO: format as embed
			},
		},
	)

	// Map for matching verbs to commands
	commandMap := make(map[string]*command)

	// Loop through commandList to get each verb
	for _, cmd := range commandList {
		for _, verb := range cmd.verbs {
			commandMap[verb] = cmd
			DebugPrint("Mapped '" + verb + "' to '" + cmd.name + "'")
		}
	}

	return commandMap
}
<commit_msg>Clean description<commit_after>package main

import (
	"fmt"
	"github.com/bwmarrin/discordgo"
	"io"
	"os"
	"strconv"
)

// generic command struct which contains name, description, and a function
type command struct {
	name             string                                                                                // human-readable name of the command
	description      string                                                                                // description of command's function
	usage            string                                                                                // example of how to correctly use command - [] for optional arguments, <> for required arguments
	verbs            []string                                                                               // all verbs which are mapped to the same command
	requiresDatabase bool                                                                                // does this command require database access?
	function         func([]string, *discordgo.Channel, *discordgo.User, *discordgo.Session) *commandOutput // function which receives a slice of arguments and returns a string to display to the user
}

// output returned by all command functions, can contain a file to be uploaded
type commandOutput struct {
	response string
	file     io.Reader
	embed    *discordgo.MessageEmbed
}

func initCommands() map[string]*command {
	commandList := []*command{}

	commandList = append(commandList,

		// Define all commands here in the order they will be displayed by the help command
		// The 'usage' field should use the default verb
		// Do not include the command prefix

		&command{
			name:             "Display help",
			description:      "Lists all commands and their purposes.\nCan also display detailed info about a given command.",
			usage:            "help [verb]",
			verbs:            []string{"help", "commands"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, user *discordgo.User, discordSession *discordgo.Session) *commandOutput {

				DebugPrint("Running help command.")

				if len(args) <= 0 {

					DebugPrint("No arguments; listing commands.")

					embed := NewEmbed().
						SetTitle("Source").
						SetAuthor("Sunbot " + version).
						SetDescription("Database enabled: " + strconv.FormatBool(redisEnabled)).
						SetURL("https://github.com/techniponi/sunbot").
						SetImage(discordSession.State.User.AvatarURL("128"))

					for _, cmd := range commandList {
						if cmd.requiresDatabase && !redisEnabled {
							// Database is not enabled, this command needs it
						} else {
							embed.AddField(cmd.name, "`"+cfg.DefaultPrefix+cmd.usage+"`")
						}
					}

					return &commandOutput{embed: embed.MessageEmbed}
				} else {

					DebugPrint("Verb was given...")

					// check if command exists
					if cmd, ok := commands[args[0]]; ok {

						embed := NewEmbed().
							SetTitle(cmd.name).
							SetDescription(cmd.description).
							AddField("Usage", "`"+cfg.DefaultPrefix+cmd.usage+"`")

						DebugPrint("Providing help for given verb.")

						// compile verbs
						verbOutput := ""
						for index, verb := range cmd.verbs {
							// don't add a comma if it's the last one
							if index == (len(cmd.verbs) - 1) {
								verbOutput += "`" + cfg.DefaultPrefix + verb + "`"
							} else {
								verbOutput += "`" + cfg.DefaultPrefix + verb + "`, "
							}
						}

						embed.AddField("Verbs", verbOutput)

						return &commandOutput{embed: embed.MessageEmbed}
					} else {
						DebugPrint("Given verb was not found.")
						return &commandOutput{response: "That isn't a valid command."}
					}
				}
			},
		},

		&command{
			name:             "Derpibooru search",
			description:      "Searches Derpibooru with the given tags as the query, chooses a random result to display.\nUse commas to separate tags like you would on the website.",
			usage:            "derpi <tags>",
			verbs:            []string{"derpi", "db", "derpibooru"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, user *discordgo.User, discordSession *discordgo.Session) *commandOutput {
				if len(args) < 1 {
					DebugPrint("User ran derpibooru command with no tags given.")
					return &commandOutput{response: "Error: no tags specified"}
				} else {
					DebugPrint("User is running derpibooru command...")

					searchQuery := ""

					for _, arg := range args {
						searchQuery += arg + " "
					}

					// enforce 'safe' tag if channel is not nsfw
					if !channel.NSFW {
						DebugPrint("Channel #" + channel.Name + " is SFW, adding safe tag...")
						searchQuery += ",safe"
					}

					DebugPrint("Searching with tags:\n" + searchQuery)

					// use derpibooru.go to perform search
					results, err := DerpiSearchWithTags(searchQuery, cfg.DerpiApiKey)
					if err != nil {
						fmt.Println(err)
						return &commandOutput{response: "Error: " + err.Error()}
					}

					// check for results
					if len(results.Search) <= 0 {
						DebugPrint("Derpibooru returned no results.")
						return &commandOutput{response: "Error: no results."}
					} else {
						DebugPrint("Derpibooru returned results; parsed successfully.")
						// pick one randomly
						output := "http:" + results.Search[RandomRange(0, len(results.Search))].Image

						return &commandOutput{response: output}
					}
				}
			},
		},

		&command{
			name:             "Gay",
			description:      "Posts a very gay image.",
			usage:            "gay",
			verbs:            []string{"gay"},
			requiresDatabase: false,
			function: func(args []string, channel *discordgo.Channel, user *discordgo.User, discordSession *discordgo.Session) *commandOutput {
				file, err := os.Open("img/gaybats.png") // TODO: move this to database; allow users to add images (permission system?)
				if err != nil {
					return &commandOutput{response: "Error opening file"}

				}
				return &commandOutput{file: file}
			},
		},

		&command{
			name:             "User stats",
			description:      "Displays the statistics of the user.",
			usage:            "stats [user]", // TODO: implement pinging users
			verbs:            []string{"stats"},
			requiresDatabase: true,
			function: func(args []string, channel *discordgo.Channel, user *discordgo.User, discordSession *discordgo.Session) *commandOutput {
				userDb, err := GetUser(user, false)
				if err != nil {
					return &commandOutput{response: "That user doesn't exist in the database yet. They need to chat some!"}
				}

				posts := userDb.Val()["posts"]
				return &commandOutput{response: "You have made " + posts + " posts! (THIS COMMAND IS WIP)"} // TODO: format as embed
			},
		},
	)

	// Map for matching verbs to commands
	commandMap := make(map[string]*command)

	// Loop through commandList to get each verb
	for _, cmd := range commandList {
		for _, verb := range cmd.verbs {
			commandMap[verb] = cmd
			DebugPrint("Mapped '" + verb + "' to '" + cmd.name + "'")
		}
	}

	return commandMap
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"fmt"
	"github.com/timakin/ts/loader"
	"github.com/codegangsta/cli"
)

var Commands = []cli.Command{
	commandAll,
	commandBiz,
	commandHack,
}

var commandAll = cli.Command{
	Name:  "all",
	Usage: "",
	Description: `
`,
	Action: doAll,
}

var commandBiz = cli.Command{
	Name:  "biz",
	Usage: "",
	Description: `
`,
	Action: doBiz,
}

var commandHack = cli.Command{
	Name:  "hack",
	Usage: "",
	Description: `
`,
	Action: doHack,
}

func debug(v ...interface{}) {
	if os.Getenv("DEBUG") != "" {
		log.Println(v...)
	}
}

func assert(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func doAll(c *cli.Context) {
	test := make(chan int)
	test2 := make(chan int)
	go loader.GoRouTest(test)
	go loader.GoRouTestTwo(test2)
	fmt.Printf("print all\n")
	result := <- test
	resTwo := <- test2
	fmt.Printf("%d%d\n", result, resTwo)
}

func doBiz(c *cli.Context) {
}

func doHack(c *cli.Context) {
}
<commit_msg>Load HN top feed<commit_after>package main

import (
	"log"
	"os"
	"fmt"
	"github.com/timakin/ts/loader"
	"github.com/codegangsta/cli"
)

var Commands = []cli.Command{
	commandAll,
	commandBiz,
	commandHack,
}

var commandAll = cli.Command{
	Name:  "all",
	Usage: "",
	Description: `
`,
	Action: doAll,
}

var commandBiz = cli.Command{
	Name:  "biz",
	Usage: "",
	Description: `
`,
	Action: doBiz,
}

var commandHack = cli.Command{
	Name:  "hack",
	Usage: "",
	Description: `
`,
	Action: doHack,
}

func debug(v ...interface{}) {
	if os.Getenv("DEBUG") != "" {
		log.Println(v...)
	}
}

func assert(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func doAll(c *cli.Context) {
	test := make(chan int)
	test2 := make(chan int)
	hn := make(chan string)
	go loader.GoRouTest(test)
	go loader.GoRouTestTwo(test2)
	go loader.GetPHFeed(hn)
	fmt.Printf("print all\n")
	result := <- test
	resTwo := <- test2
	phres := <- hn
	fmt.Printf("%d%d\n", result, resTwo)
	fmt.Printf(phres)
}

func doBiz(c *cli.Context) {
}

func doHack(c *cli.Context) {
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"golang.org/x/oauth2"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
	"strings"
)

var DefaultFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "data",
		Usage: "Body data",
	},
}

var Commands = []cli.Command{
	commandGet,
	commandPost,
	commandPut,
	commandDelete,
}

var commandGet = cli.Command{
	Name:      "get",
	ShortName: "g",
	Usage:     "Make GET request",
	Description: `
`,
	Flags:  DefaultFlags,
	Action: doGet,
}

var commandPost = cli.Command{
	Name:      "post",
	ShortName: "p",
	Usage:     "Make POST request",
	Description: `
`,
	Flags:  DefaultFlags,
	Action: doPost,
}

var commandPut = cli.Command{
	Name:  "put",
	Usage: "Make PUT request",
	Description: `
`,
	Flags:  DefaultFlags,
	Action: doPut,
}

var commandDelete = cli.Command{
	Name:      "delete",
	ShortName: "d",
	Usage:     "Make DELETE request",
	Description: `
`,
	Flags:  DefaultFlags,
	Action: doDelete,
}

func assert(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func doGet(ctx *cli.Context) {
	loadOptions(ctx)
	doRequest(ctx, "GET")
}

func doPost(ctx *cli.Context) {
	loadOptions(ctx)
	doRequest(ctx, "POST")
}

func doPut(ctx *cli.Context) {
	loadOptions(ctx)
	doRequest(ctx, "POST")
}

func doDelete(ctx *cli.Context) {
	loadOptions(ctx)
	doRequest(ctx, "DELETE")
}

func loadOptions(ctx *cli.Context) {
	var err error
	CurrentOptions, err = Opts(ctx)
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
}

func doRequest(ctx *cli.Context, method string) {
	Tracef("doRequest start")
	result, tok, err := doRequest0(ctx, method)
	if err != nil {
		Tracef("error found on request")
		log.Fatal(err)
	} else {
		Tracef("request done successfully")
		if tok != nil {
			storeToken(tok)
		}
	}
	if result != nil {
		Tracef("result found")
		fmt.Println(string(result))
	} else {
		Tracef("no result")
	}
	Tracef("doRequest end")
}

func doRequest0(ctx *cli.Context, method string) ([]byte, *oauth2.Token, error) {
	Tracef("profileName = %s", CurrentOptions.ProfileName)

	targetUrl, err := targetUrl(ctx)
	if err != nil {
		return nil, nil, err
	}
	Tracef("targetUrl = %s", targetUrl)
	data := ctx.String("data")
	Tracef("data = %s", data)
	body := strings.NewReader(data)
	req, err := http.NewRequest(method, targetUrl, body)
	if err != nil {
		return nil, nil, err
	}

	var lastError error
	for retrieve := false; retrieve == false; retrieve = true {
		Tracef("=== phase %s start", toString(retrieve))
		tok, r, err := AccessToken(CurrentOptions.ProfileName, retrieve)
		if err != nil {
			Tracef("phase %s failed (a)", toString(retrieve))
			lastError = err
			continue
		}
		retrieve = r
		req.Header.Set("User-Agent", fmt.Sprintf("%s-%s", ctx.App.Name, Version))
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tok.AccessToken))
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			Tracef("phase %s failed (b)", toString(retrieve))
			lastError = err
			continue
		}
		Tracef("request = %s", string(dump))

		client := new(http.Client)
		resp, err := client.Do(req)
		if err != nil {
			Tracef("phase %s failed (c)", toString(retrieve))
			lastError = err
			continue
		}

		dumpResp, err := httputil.DumpResponse(resp, true)
		if err != nil {
			log.Printf("%+v%n", err)
		} else {
			Tracef("response = %s", string(dumpResp))
		}

		Tracef("phase %s", toString(retrieve))
		if resp.StatusCode >= 400 && resp.StatusCode < 500 && retrieve {
			Tracef("phase %s failed (d)", toString(retrieve))
			lastError = err
			break
		}

		Tracef("read body %s", toString(retrieve))
		body, err := ioutil.ReadAll(resp.Body)
		return body, tok, err
	}
	return nil, nil, fmt.Errorf("%v", lastError)
}

func toString(retrieve bool) string {
	if retrieve {
		return "retrieve"
	}
	return "sotred"
}

func targetUrl(ctx *cli.Context) (string, error) {
	if len(ctx.Args()) < 1 {
		return "", fmt.Errorf("target URL required")
	}
	return ctx.Args()[0], nil
}

func storeToken(tok *oauth2.Token) {
	if SaveValues(CurrentOptions.ProfileName, tokenToValues(tok)) {
		Tracef("token stored [%s]", CurrentOptions.ProfileName)
	} else {
		Tracef("fail to store token [%s]", CurrentOptions.ProfileName)
	}
}
<commit_msg>set headers when occuring redirect<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"golang.org/x/oauth2"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
	"strings"
)

var DefaultFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "data",
		Usage: "Body data",
	},
}

var Commands = []cli.Command{
	commandGet,
	commandPost,
	commandPut,
	commandDelete,
}

var commandGet = cli.Command{
	Name:      "get",
	ShortName: "g",
	Usage:     "Make GET request",
	Description: `
`,
	Flags:  DefaultFlags,
	Action: doGet,
}

var commandPost = cli.Command{
	Name:      "post",
	ShortName: "p",
	Usage:     "Make POST request",
	Description: `
`,
	Flags:  DefaultFlags,
	Action: doPost,
}

var commandPut = cli.Command{
	Name:  "put",
	Usage: "Make PUT request",
	Description: `
`,
	Flags:  DefaultFlags,
	Action: doPut,
}

var commandDelete = cli.Command{
	Name:      "delete",
	ShortName: "d",
	Usage:     "Make DELETE request",
	Description: `
`,
	Flags:  DefaultFlags,
	Action: doDelete,
}

func assert(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func doGet(ctx *cli.Context) {
	loadOptions(ctx)
	doRequest(ctx, "GET")
}

func doPost(ctx *cli.Context) {
	loadOptions(ctx)
	doRequest(ctx, "POST")
}

func doPut(ctx *cli.Context) {
	loadOptions(ctx)
	doRequest(ctx, "POST")
}

func doDelete(ctx *cli.Context) {
	loadOptions(ctx)
	doRequest(ctx, "DELETE")
}

func loadOptions(ctx *cli.Context) {
	var err error
	CurrentOptions, err = Opts(ctx)
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
}

func doRequest(ctx *cli.Context, method string) {
	Tracef("doRequest start")
	result, tok, err := doRequest0(ctx, method)
	if err != nil {
		Tracef("error found on request")
		log.Fatal(err)
	} else {
		Tracef("request done successfully")
		if tok != nil {
			storeToken(tok)
		}
	}
	if result != nil {
		Tracef("result found")
		fmt.Println(string(result))
	} else {
		Tracef("no result")
	}
	Tracef("doRequest end")
}

func doRequest0(ctx *cli.Context, method string) ([]byte, *oauth2.Token, error) {
	Tracef("profileName = %s", CurrentOptions.ProfileName)

	targetUrl, err := targetUrl(ctx)
	if err != nil {
		return nil, nil, err
	}
	Tracef("targetUrl = %s", targetUrl)
	data := ctx.String("data")
	Tracef("data = %s", data)
	body := strings.NewReader(data)
	req, err := http.NewRequest(method, targetUrl, body)
	if err != nil {
		return nil, nil, err
	}

	var lastError error
	for retrieve := false; retrieve == false; retrieve = true {
		Tracef("=== phase %s start", toString(retrieve))
		tok, r, err := AccessToken(CurrentOptions.ProfileName, retrieve)
		if err != nil {
			Tracef("phase %s failed (token retrieving failed)", toString(retrieve))
			lastError = err
			continue
		}
		retrieve = r
		req.Header.Set("User-Agent", fmt.Sprintf("%s-%s", ctx.App.Name, Version))
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tok.AccessToken))
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			Tracef("phase %s failed (request dump failed)", toString(retrieve))
			lastError = err
			continue
		}
		Tracef("request = %s", string(dump))

		client := &http.Client{CheckRedirect: func(req *http.Request, via []*http.Request) error {
			Tracef("redirect to %s", req.URL.String())
			req.Header.Set("User-Agent", fmt.Sprintf("%s-%s", ctx.App.Name, Version))
			req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tok.AccessToken))
			return nil
		}}
		resp, err := client.Do(req)
		if err != nil {
			Tracef("phase %s failed (request failed)", toString(retrieve))
			lastError = err
			continue
		}

		dumpResp, err := httputil.DumpResponse(resp, true)
		if err != nil {
			log.Printf("%+v%n", err)
		} else {
			Tracef("response = %s", string(dumpResp))
		}

		Tracef("phase %s", toString(retrieve))
		if resp.StatusCode >= 400 && resp.StatusCode < 500 {
			if retrieve {
				Tracef("phase retrieve failed (4XX response)")
				lastError = err
				break
			} else {
				Tracef("phase stored failed (4XX response) -> final result")
			}
		}

		Tracef("read body %s", toString(retrieve))
		body, err := ioutil.ReadAll(resp.Body)
		return body, tok, err
	}
	return nil, nil, fmt.Errorf("%v", lastError)
}

func toString(retrieve bool) string {
	if retrieve {
		return "retrieve"
	}
	return "sotred"
}

func targetUrl(ctx *cli.Context) (string, error) {
	if len(ctx.Args()) < 1 {
		return "", fmt.Errorf("target URL required")
	}
	return ctx.Args()[0], nil
}

func storeToken(tok *oauth2.Token) {
	if SaveValues(CurrentOptions.ProfileName, tokenToValues(tok)) {
		Tracef("token stored [%s]", CurrentOptions.ProfileName)
	} else {
		Tracef("fail to store token [%s]", CurrentOptions.ProfileName)
	}
}
<|endoftext|>


<commit_before>package dummy

import (
	//"strings"

	"github.com/tendermint/abci/types"
	"github.com/tendermint/merkleeyes/iavl"
	cmn "github.com/tendermint/tmlibs/common"
	"github.com/tendermint/tmlibs/merkle"
	"github.com/tendermint/go-wire"
	"github.com/bitly/go-simplejson"

	"fmt"
	"os/exec"
	"os"
	"io/ioutil"
)

type DummyApplication struct {
	types.BaseApplication

	state merkle.Tree
}
// Transaction type bytes
const (
	WriteSet byte = 0x01
	WriteRem byte = 0x02
)
func NewDummyApplication() *DummyApplication {
	state := iavl.NewIAVLTree(0, nil)
	return &DummyApplication{state: state}
}

func (app *DummyApplication) Info() (resInfo types.ResponseInfo) {
	return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
}

// tx is either "key=value" or just arbitrary bytes
func (app *DummyApplication) DeliverTx(tx []byte) types.Result {
	//parts := strings.Split(string(tx), "=")
	//if len(parts) == 2 {
	//	app.state.Set([]byte(parts[0]), []byte(parts[1]))
	//} else {
	//	app.state.Set(tx, tx)
	//}
	//return types.OK
	tree := app.state
	return app.doTx(tree, tx)
}
func (app *DummyApplication) doTx(tree merkle.Tree, tx []byte) types.Result {
	if len(tx) == 0 {
		return types.ErrEncodingError.SetLog("Tx length cannot be zero")
	}
	typeByte := tx[0]
	tx = tx[1:]
	switch typeByte {
	case WriteSet: // Set
		key, n, err := wire.GetByteSlice(tx)
		if err != nil {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Error reading key: %v", err.Error()))
		}
		tx = tx[n:]
		value, n, err := wire.GetByteSlice(tx)
		if err != nil {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Error reading value: %v", err.Error()))
		}
		tx = tx[n:]
		if len(tx) != 0 {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Got bytes left over"))
		}

		tree.Set(key, value)
		//case WriteRem: // Remove
		//	key, n, err := wire.GetByteSlice(tx)
		//	if err != nil {
		//		return types.ErrEncodingError.SetLog(cmn.Fmt("Error reading key: %v", err.Error()))
		//	}
		//	tx = tx[n:]
		//	if len(tx) != 0 {
		//		return types.ErrEncodingError.SetLog(cmn.Fmt("Got bytes left over"))
		//	}
		//	tree.Remove(key)
	case WriteRem: // Compare
		key, n, err := wire.GetByteSlice(tx)
		if err != nil {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Error reading key: %v", err.Error()))
		}
		tx = tx[n:]
		value, n, err := wire.GetByteSlice(tx)
		if err != nil {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Error reading value: %v", err.Error()))
		}
		tx = tx[n:]
		if len(tx) != 0 {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Got bytes left over"))
		}

		//获得IPFS地址
		_, stuValue, stuExists := app.state.Get(value)
		_, pojValue, pojExists := app.state.Get(key)

		//判断两个地址都存存在
		if stuExists && pojExists{
			matched := Compare(string(stuValue),string(pojValue))
			if matched {
				fmt.Println("matched")
				return types.NewResultOK([]byte("Matched"),"log")
			}else{
				fmt.Println("not matched")
				return types.OK
			}
		} else {
			return types.ErrUnknownRequest.SetLog(cmn.Fmt("Unexpected Account %X", key))

		}

	default:
		return types.ErrUnknownRequest.SetLog(cmn.Fmt("Unexpected Tx type byte %X", typeByte))
	}
	return types.OK
}

func (app *DummyApplication) CheckTx(tx []byte) types.Result {
	//return types.OK
	tree := app.state
	return app.doTx(tree, tx)
}

func (app *DummyApplication) Commit() types.Result {
	hash := app.state.Hash()
	return types.NewResultOK(hash, "")
}
func ipfsDownload(add string,path string) {

	var (
		cmdOut []byte
		err    error
	)

	cmd := "ipfs"

	args := []string{"get",add,"-o",path}

	if cmdOut, err = exec.Command(cmd, args...).Output(); err != nil {
		fmt.Fprintln(os.Stderr, "There was an error running git rev-parse command: ", err)
		os.Exit(1)
	}
	result := string(cmdOut)

	fmt.Println(result)
	//fmt.Println("get result",sequenceInt)
}

func getIntItem(path string,item string) int{

	dat, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	//fmt.Print(string(dat))
	js, err :=simplejson.NewJson([]byte(dat))
	majorStr := js.Get("criteria").Get(item).MustInt()
	fmt.Println("---",majorStr)
	return majorStr
}

func getStringItem(path string,item string) string{

	dat, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}
	//fmt.Print(string(dat))
	js, err :=simplejson.NewJson([]byte(dat))
	majorStr := js.Get("criteria").Get(item).MustString()
	fmt.Println("---",majorStr)
	return majorStr
}
/*
比较提交的文件是否与要求匹配
input: criteria 条件json ；target：申请人条件文件
output: 结果 bool
*/
func compareFiles(criteria string, target string) bool{

	////年龄小于限定值
	//ageC := getIntItem(criteria,"age")
	//fmt.Println("age: ",ageC)
	//
	//ageS := getIntItem(target,"age")
	//fmt.Println("age: ",ageS)

	// 排名在要求之前
	rankC := getIntItem(criteria,"rank")
	fmt.Println("rank: ",rankC)

	rankS := getIntItem(target,"rank")
	fmt.Println("rank: ",rankS)

	//专业一致
	majorC := getStringItem(criteria,"major")
	fmt.Println("major: ",majorC)

	majorS := getStringItem(target,"major")
	fmt.Println("major: ",majorS)

	if rankC>rankS && majorC ==majorS {
		return true
	}else{
		return false
	}

}

func Compare(studentAdd string,projectAdd string) bool{

	ipfsDownload(studentAdd,"/Users/b/Documents/")
	ipfsDownload(projectAdd,"/Users/b/Documents/")

	filepath2 := "/Users/b/Documents/"+studentAdd
	filepath := "/Users/b/Documents/"+projectAdd

	result := compareFiles(filepath,filepath2)
	fmt.Println("get result", result)
	//return types.NewResultOK([]byte("OK"), "")
	return result
}
func (app *DummyApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
	if reqQuery.Prove {
		value, proof, exists := app.state.Proof(reqQuery.Data)
		resQuery.Index = -1 // TODO make Proof return index
		resQuery.Key = reqQuery.Data
		resQuery.Value = value
		resQuery.Proof = proof
		if exists {
			resQuery.Log = "exists"
		} else {
			resQuery.Log = "does not exist"
		}
		return
	} else {
		index, value, exists := app.state.Get(reqQuery.Data)
		resQuery.Index = int64(index)
		resQuery.Value = value
		if exists {
			resQuery.Log = "exists"
		} else {
			resQuery.Log = "does not exist"
		}
		return
	}
}
<commit_msg>Add function to check IPFS files when doing Check_tx<commit_after>package dummy

import (
	//"strings"

	"github.com/tendermint/abci/types"
	"github.com/tendermint/merkleeyes/iavl"
	cmn "github.com/tendermint/tmlibs/common"
	"github.com/tendermint/tmlibs/merkle"
	"github.com/tendermint/go-wire"

)

type DummyApplication struct {
	types.BaseApplication

	state merkle.Tree
}
// Transaction type bytes
const (
	WriteSet byte = 0x01
	WriteRem byte = 0x02
)
func NewDummyApplication() *DummyApplication {
	state := iavl.NewIAVLTree(0, nil)
	return &DummyApplication{state: state}
}

func (app *DummyApplication) Info() (resInfo types.ResponseInfo) {
	return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
}

// tx is either "key=value" or just arbitrary bytes
func (app *DummyApplication) DeliverTx(tx []byte) types.Result {
	//parts := strings.Split(string(tx), "=")
	//if len(parts) == 2 {
	//	app.state.Set([]byte(parts[0]), []byte(parts[1]))
	//} else {
	//	app.state.Set(tx, tx)
	//}
	//return types.OK
	tree := app.state
	return app.doTx(tree, tx)
}
func (app *DummyApplication) doTx(tree merkle.Tree, tx []byte) types.Result {
	if len(tx) == 0 {
		return types.ErrEncodingError.SetLog("Tx length cannot be zero")
	}
	typeByte := tx[0]
	tx = tx[1:]
	switch typeByte {
	case WriteSet: // Set
		key, n, err := wire.GetByteSlice(tx)
		if err != nil {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Error reading key: %v", err.Error()))
		}
		tx = tx[n:]
		value, n, err := wire.GetByteSlice(tx)
		if err != nil {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Error reading value: %v", err.Error()))
		}
		tx = tx[n:]
		if len(tx) != 0 {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Got bytes left over"))
		}

		tree.Set(key, value)
	case WriteRem: // Remove
		key, n, err := wire.GetByteSlice(tx)
		if err != nil {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Error reading key: %v", err.Error()))
		}
		tx = tx[n:]
		if len(tx) != 0 {
			return types.ErrEncodingError.SetLog(cmn.Fmt("Got bytes left over"))
		}
		tree.Remove(key)
	default:
		return types.ErrUnknownRequest.SetLog(cmn.Fmt("Unexpected Tx type byte %X", typeByte))
	}
	return types.OK
}

func (app *DummyApplication) CheckTx(tx []byte) types.Result {
	//return types.OK
	tree := app.state
	return app.doTx(tree, tx)
}

func (app *DummyApplication) Commit() types.Result {
	hash := app.state.Hash()
	return types.NewResultOK(hash, "")
}

func (app *DummyApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
	if reqQuery.Prove {
		value, proof, exists := app.state.Proof(reqQuery.Data)
		resQuery.Index = -1 // TODO make Proof return index
		resQuery.Key = reqQuery.Data
		resQuery.Value = value
		resQuery.Proof = proof
		if exists {
			resQuery.Log = "exists"
		} else {
			resQuery.Log = "does not exist"
		}
		return
	} else {
		index, value, exists := app.state.Get(reqQuery.Data)
		resQuery.Index = int64(index)
		resQuery.Value = value
		if exists {
			resQuery.Log = "exists"
		} else {
			resQuery.Log = "does not exist"
		}
		return
	}
}
<|endoftext|>


<commit_before>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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.

package ogletest

import (
	"testing"
)

////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////

type fakeAnyOfMatcher struct {
	desc string
	res  MatchResult
	err  string
}

func (m *fakeAnyOfMatcher) Matches(c interface{}) (MatchResult, string) {
	return m.res, m.err
}

func (m *fakeAnyOfMatcher) Description() string {
	return m.desc
}

////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////

func TestAnyOfCallsWrapped(t *testing.T) {
	t.Fail()
}

func TestEmptySet(t *testing.T) {
	matcher := AnyOf()

	res, err := matcher.Matches(0)
	expectedRes := MATCH_FALSE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneTrue(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		&fakeAnyOfMatcher{"", MATCH_TRUE, ""},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_TRUE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneEqual(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "foo"},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		13,
		"taco",
		19,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches("taco")
	expectedRes := MATCH_TRUE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneUndefined(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "taco"},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_UNDEFINED
	expectedErr := "taco"

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestAllFalseAndNotEqual(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		19,
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_FALSE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestDescriptionForEmptySet(t *testing.T) {
	matcher := AnyOf()
	desc := matcher.Description()
	expected := "or()"

	if desc != expected {
		t.Errorf("Expected %v, got %v", expected, desc)
	}
}

func TestDescriptionForNonEmptySet(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"taco", MATCH_TRUE, ""},
		"burrito",
		&fakeAnyOfMatcher{"enchilada", MATCH_TRUE, ""},
	)

	desc := matcher.Description()
	expected := "or(taco, burrito, enchilada)"

	if desc != expected {
		t.Errorf("Expected %v, got %v", expected, desc)
	}
}
<commit_msg>Removed a test function.<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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.

package ogletest

import (
	"testing"
)

////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////

type fakeAnyOfMatcher struct {
	desc string
	res  MatchResult
	err  string
}

func (m *fakeAnyOfMatcher) Matches(c interface{}) (MatchResult, string) {
	return m.res, m.err
}

func (m *fakeAnyOfMatcher) Description() string {
	return m.desc
}

////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////

func TestEmptySet(t *testing.T) {
	matcher := AnyOf()

	res, err := matcher.Matches(0)
	expectedRes := MATCH_FALSE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneTrue(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		&fakeAnyOfMatcher{"", MATCH_TRUE, ""},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_TRUE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneEqual(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "foo"},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		13,
		"taco",
		19,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches("taco")
	expectedRes := MATCH_TRUE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestOneUndefined(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_UNDEFINED, "taco"},
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_UNDEFINED
	expectedErr := "taco"

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestAllFalseAndNotEqual(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		17,
		&fakeAnyOfMatcher{"", MATCH_FALSE, "foo"},
		19,
	)

	res, err := matcher.Matches(0)
	expectedRes := MATCH_FALSE
	expectedErr := ""

	if res != expectedRes {
		t.Errorf("Expected %v, got %v", expectedRes, res)
	}

	if err != expectedErr {
		t.Errorf("Expected %v, got %v", expectedErr, err)
	}
}

func TestDescriptionForEmptySet(t *testing.T) {
	matcher := AnyOf()
	desc := matcher.Description()
	expected := "or()"

	if desc != expected {
		t.Errorf("Expected %v, got %v", expected, desc)
	}
}

func TestDescriptionForNonEmptySet(t *testing.T) {
	matcher := AnyOf(
		&fakeAnyOfMatcher{"taco", MATCH_TRUE, ""},
		"burrito",
		&fakeAnyOfMatcher{"enchilada", MATCH_TRUE, ""},
	)

	desc := matcher.Description()
	expected := "or(taco, burrito, enchilada)"

	if desc != expected {
		t.Errorf("Expected %v, got %v", expected, desc)
	}
}
<|endoftext|>


<commit_before>package aoj

import (
	"encoding/xml"
	"github.com/zakuro9715/AOJRESTAPI/util/http"
	"net/url"
	"strconv"
	"strings"
)

type Problem struct {
	Id                 string
	Name               string
	Available          int
	ProblemTimeLimit   int
	ProblemMemoryLimit int
	Status             Status
	SolvedList         []SolvedUser
}

type SolvedUser struct {
	Id             string
	SubmissionDate int64
	Language       string
	CpuTime        int
	Memory         int
	CodeSize       int
}

const (
	ProblemSearchUrl = "http://judge.u-aizu.ac.jp/onlinejudge/webservice/problem"
)

func (p *Problem) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	pp := struct {
		Id                 string       `xml:"id"`
		Name               string       `xml:"name"`
		Available          string       `xml:"available"`
		ProblemTimeLimit   string       `xml:"problemtimelimit"`
		ProblemMemoryLimit string       `xml:"problemmemorylimit"`
		Status             Status       `xml:"status"`
		SolvedList         []SolvedUser `xml:"solved_list>user"`
	}{}
	if err := d.DecodeElement(&pp, &start); err != nil {
		return err
	}

	available, err := strconv.Atoi(strings.Trim(pp.Available, "\n"))
	problemTimeLimit, err := strconv.Atoi(strings.Trim(pp.ProblemTimeLimit, "\n"))
	problemMemoryLimit, err := strconv.Atoi(strings.Trim(pp.ProblemMemoryLimit, "\n"))
	if err != nil {
		return err
	}

	*p = Problem{
		Id:                 strings.Trim(pp.Id, "\n"),
		Name:               strings.Trim(pp.Name, "\n"),
		Available:          available,
		ProblemTimeLimit:   problemTimeLimit,
		ProblemMemoryLimit: problemMemoryLimit,
		Status:             pp.Status,
		SolvedList:         pp.SolvedList,
	}
	return nil
}

func (s *SolvedUser) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	ss := struct {
		Id             string `xml:"id"`
		SubmissionDate string `xml:"submissiondate"`
		Language       string `xml:"language"`
		CpuTime        string `xml:"cputime"`
		Memory         string `xml:"memory"`
		CodeSize       string `xml:"code_size"`
	}{}

	if err := d.DecodeElement(&ss, &start); err != nil {
		return err
	}

	submissionDate, _ := strconv.ParseInt(strings.Trim(ss.SubmissionDate, "\n "), 10, 64)
	cpuTime, _ := strconv.Atoi(strings.Trim(ss.CpuTime, "\n "))
	memory, _ := strconv.Atoi(strings.Trim(ss.Memory, "\n "))
	codeSize, _ := strconv.Atoi(strings.Trim(ss.CodeSize, "\n "))
	*s = SolvedUser{
		Id:             strings.Trim(ss.Id, "\n "),
		SubmissionDate: submissionDate,
		Language:       strings.Trim(ss.Language, "\n "),
		CpuTime:        cpuTime,
		Memory:         memory,
		CodeSize:       codeSize,
	}
	return nil
}

func ProblemSearchApi(id string, status bool) (*Problem, error) {
	q := url.Values{"id": {id}, "status": {strconv.FormatBool(status)}}.Encode()
	data, err := http.Get(ProblemSearchUrl, q)
	if err != nil {
		return nil, err
	}
	p := new(Problem)
	err = xml.Unmarshal(data, p)
	return p, err
}
<commit_msg>Add ProblemListSearchApi<commit_after>package aoj

import (
	"encoding/xml"
	"github.com/zakuro9715/AOJRESTAPI/util/http"
	"net/url"
	"strconv"
	"strings"
)

type Problem struct {
	Id                 string
	Name               string
	Available          int
	ProblemTimeLimit   int
	ProblemMemoryLimit int
	Status             Status
	SolvedList         []SolvedUser
}

type SolvedUser struct {
	Id             string
	SubmissionDate int64
	Language       string
	CpuTime        int
	Memory         int
	CodeSize       int
}

const (
	ProblemSearchUrl     = "http://judge.u-aizu.ac.jp/onlinejudge/webservice/problem"
	ProblemListSearchUrl = "http://judge.u-aizu.ac.jp/onlinejudge/webservice/problem_list"
)

func (p *Problem) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	pp := struct {
		Id                 string       `xml:"id"`
		Name               string       `xml:"name"`
		Available          string       `xml:"available"`
		ProblemTimeLimit   string       `xml:"problemtimelimit"`
		ProblemMemoryLimit string       `xml:"problemmemorylimit"`
		Status             Status       `xml:"status"`
		SolvedList         []SolvedUser `xml:"solved_list>user"`
	}{}
	if err := d.DecodeElement(&pp, &start); err != nil {
		return err
	}

	available, err := strconv.Atoi(strings.Trim(pp.Available, "\n"))
	problemTimeLimit, err := strconv.Atoi(strings.Trim(pp.ProblemTimeLimit, "\n"))
	problemMemoryLimit, err := strconv.Atoi(strings.Trim(pp.ProblemMemoryLimit, "\n"))
	if err != nil {
		return err
	}

	*p = Problem{
		Id:                 strings.Trim(pp.Id, "\n"),
		Name:               strings.Trim(pp.Name, "\n"),
		Available:          available,
		ProblemTimeLimit:   problemTimeLimit,
		ProblemMemoryLimit: problemMemoryLimit,
		Status:             pp.Status,
		SolvedList:         pp.SolvedList,
	}
	return nil
}

func (s *SolvedUser) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	ss := struct {
		Id             string `xml:"id"`
		SubmissionDate string `xml:"submissiondate"`
		Language       string `xml:"language"`
		CpuTime        string `xml:"cputime"`
		Memory         string `xml:"memory"`
		CodeSize       string `xml:"code_size"`
	}{}

	if err := d.DecodeElement(&ss, &start); err != nil {
		return err
	}

	submissionDate, _ := strconv.ParseInt(strings.Trim(ss.SubmissionDate, "\n "), 10, 64)
	cpuTime, _ := strconv.Atoi(strings.Trim(ss.CpuTime, "\n "))
	memory, _ := strconv.Atoi(strings.Trim(ss.Memory, "\n "))
	codeSize, _ := strconv.Atoi(strings.Trim(ss.CodeSize, "\n "))
	*s = SolvedUser{
		Id:             strings.Trim(ss.Id, "\n "),
		SubmissionDate: submissionDate,
		Language:       strings.Trim(ss.Language, "\n "),
		CpuTime:        cpuTime,
		Memory:         memory,
		CodeSize:       codeSize,
	}
	return nil
}

func ProblemSearchApi(id string, status bool) (*Problem, error) {
	q := url.Values{"id": {id}, "status": {strconv.FormatBool(status)}}.Encode()
	data, err := http.Get(ProblemSearchUrl, q)
	if err != nil {
		return nil, err
	}
	p := new(Problem)
	err = xml.Unmarshal(data, p)
	return p, err
}

func ProblemListSearchApi(volume int) ([]Problem, error) {
	q := url.Values{"volume": {strconv.Itoa(volume)}}.Encode()
	data, err := http.Get(ProblemListSearchUrl, q)
	if err != nil {
		return nil, err
	}
	pl := &struct {
		Problems []Problem `xml:"problem"`
	}{}
	err = xml.Unmarshal(data, pl)
	return pl.Problems, err
}
<|endoftext|>