<commit_before>package gitlab

import (
	"fmt"
	"time"
)

// EpicsService handles communication with the epic related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type EpicsService struct {
	client *Client
}

// EpicAuthor represents a author of the epic.
type EpicAuthor struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// Epic represents a GitLab epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type Epic struct {
	ID                      int         `json:"id"`
	IID                     int         `json:"iid"`
	GroupID                 int         `json:"group_id"`
	Author                  *EpicAuthor `json:"author"`
        ParentID		int	    `json:"parent_id"`
	Description             string      `json:"description"`
	State                   string      `json:"state"`
	WebURL                  string      `json:"web_url"`
	Upvotes                 int         `json:"upvotes"`
	Downvotes               int         `json:"downvotes"`
	Labels                  []string    `json:"labels"`
	Title                   string      `json:"title"`
	UpdatedAt               *time.Time  `json:"updated_at"`
	CreatedAt               *time.Time  `json:"created_at"`
	UserNotesCount          int         `json:"user_notes_count"`
	StartDate               *ISOTime    `json:"start_date"`
	StartDateIsFixed        bool        `json:"start_date_is_fixed"`
	StartDateFixed          *ISOTime    `json:"start_date_fixed"`
	StartDateFromMilestones *ISOTime    `json:"start_date_from_milestones"`
	DueDate                 *ISOTime    `json:"due_date"`
	DueDateIsFixed          bool        `json:"due_date_is_fixed"`
	DueDateFixed            *ISOTime    `json:"due_date_fixed"`
	DueDateFromMilestones   *ISOTime    `json:"due_date_from_milestones"`
}

func (e Epic) String() string {
	return Stringify(e)
}

// ListGroupEpicsOptions represents the available ListGroupEpics() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
type ListGroupEpicsOptions struct {
	ListOptions
	AuthorID                *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
	Labels                  Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
	WithLabelDetails        *bool      `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
	OrderBy                 *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort                    *string    `url:"sort,omitempty" json:"sort,omitempty"`
	Search                  *string    `url:"search,omitempty" json:"search,omitempty"`
	State                   *string    `url:"state,omitempty" json:"state,omitempty"`
	CreatedAfter            *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore           *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
	UpdatedAfter            *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore           *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	IncludeAncestorGroups   *bool      `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
	IncludeDescendantGroups *bool      `url:"include_descendant_groups,omitempty" json:"include_descendant_groups,omitempty"`
	MyReactionEmoji         *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
}

// ListGroupEpics gets a list of group epics. This function accepts pagination
// parameters page and per_page to return the list of group epics.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var es []*Epic
	resp, err := s.client.Do(req, &es)
	if err != nil {
		return nil, resp, err
	}

	return es, resp, err
}

// GetEpic gets a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic
func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// GetEpicLinks gets all child epics of an epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_links.html
func (s *EpicsService) GetEpicLinks(gid interface{}, epic int, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/epics", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var e []*Epic
	resp, err := s.client.Do(req, &e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// CreateEpicOptions represents the available CreateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
type CreateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
}

// CreateEpic creates a new group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// UpdateEpicOptions represents the available UpdateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
type UpdateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
	StateEvent       *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
}

// UpdateEpic updates an existing group epic. This function is also used
// to mark an epic as closed.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

func (s *EpicsService) GetEpicLinks(gid interface{}, epic int, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/epics", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var e []*Epic
	resp, err := s.client.Do(req, &e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// DeleteEpic deletes a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic
func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<commit_msg>Removed Duplicate<commit_after>package gitlab

import (
	"fmt"
	"time"
)

// EpicsService handles communication with the epic related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type EpicsService struct {
	client *Client
}

// EpicAuthor represents a author of the epic.
type EpicAuthor struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// Epic represents a GitLab epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type Epic struct {
	ID                      int         `json:"id"`
	IID                     int         `json:"iid"`
	GroupID                 int         `json:"group_id"`
	Author                  *EpicAuthor `json:"author"`
        ParentID		int	    `json:"parent_id"`
	Description             string      `json:"description"`
	State                   string      `json:"state"`
	WebURL                  string      `json:"web_url"`
	Upvotes                 int         `json:"upvotes"`
	Downvotes               int         `json:"downvotes"`
	Labels                  []string    `json:"labels"`
	Title                   string      `json:"title"`
	UpdatedAt               *time.Time  `json:"updated_at"`
	CreatedAt               *time.Time  `json:"created_at"`
	UserNotesCount          int         `json:"user_notes_count"`
	StartDate               *ISOTime    `json:"start_date"`
	StartDateIsFixed        bool        `json:"start_date_is_fixed"`
	StartDateFixed          *ISOTime    `json:"start_date_fixed"`
	StartDateFromMilestones *ISOTime    `json:"start_date_from_milestones"`
	DueDate                 *ISOTime    `json:"due_date"`
	DueDateIsFixed          bool        `json:"due_date_is_fixed"`
	DueDateFixed            *ISOTime    `json:"due_date_fixed"`
	DueDateFromMilestones   *ISOTime    `json:"due_date_from_milestones"`
}

func (e Epic) String() string {
	return Stringify(e)
}

// ListGroupEpicsOptions represents the available ListGroupEpics() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
type ListGroupEpicsOptions struct {
	ListOptions
	AuthorID                *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
	Labels                  Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
	WithLabelDetails        *bool      `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
	OrderBy                 *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort                    *string    `url:"sort,omitempty" json:"sort,omitempty"`
	Search                  *string    `url:"search,omitempty" json:"search,omitempty"`
	State                   *string    `url:"state,omitempty" json:"state,omitempty"`
	CreatedAfter            *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore           *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
	UpdatedAfter            *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore           *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	IncludeAncestorGroups   *bool      `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
	IncludeDescendantGroups *bool      `url:"include_descendant_groups,omitempty" json:"include_descendant_groups,omitempty"`
	MyReactionEmoji         *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
}

// ListGroupEpics gets a list of group epics. This function accepts pagination
// parameters page and per_page to return the list of group epics.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var es []*Epic
	resp, err := s.client.Do(req, &es)
	if err != nil {
		return nil, resp, err
	}

	return es, resp, err
}

// GetEpic gets a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic
func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// GetEpicLinks gets all child epics of an epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_links.html
func (s *EpicsService) GetEpicLinks(gid interface{}, epic int, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/epics", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var e []*Epic
	resp, err := s.client.Do(req, &e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// CreateEpicOptions represents the available CreateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
type CreateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
}

// CreateEpic creates a new group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// UpdateEpicOptions represents the available UpdateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
type UpdateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
	StateEvent       *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
}

// UpdateEpic updates an existing group epic. This function is also used
// to mark an epic as closed.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// DeleteEpic deletes a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic
func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<|endoftext|>


<commit_before>// Package eport provides a simple API to write Erlang ports in Go.
//
// For more information on Erlang ports see http://www.erlang.org/doc/tutorial/c_port.html.
package eport

import (
	"errors"
)

type Port interface {
	// Read reads up to len(p) bytes into p.
	// It returns the number of bytes read (0 <= n <= len(p)) and any
	// error encountered.
	//
	// In case of line (or packet) based port, receiving a line (packet) of size
	// bigger than len(p) will skip the line (packet) and return n=0 and err=ErrTooBig.
	// It is up to the caller to choose whether to consider this as a fatal error
	// or to continue reading.
	Read(p []byte) (n int, err error)
	// ReadOne reads either one packet, one line (ending with '\n') or a byte
	// from a packet, line or stream-based port accordingly.
	ReadOne() (data []byte, err error)
	// Write writes len(data) bytes from data to the port.
	// It returns the number of bytes written from data (0 <= n <= len(data))
	// and any error encountered that caused the write to stop early.
	// Write must return a non-nil error if it returns n < len(data).
	Write(data []byte) (n int, err error)
}

var (
	ErrBadSizeLen   = errors.New("eport: bad 'packet size' length")
	ErrSizeOverflow = errors.New("eport: packet size overflow")
	ErrTooBig       = errors.New("eport: packet does not fit the buffer")
)
<commit_msg>use io.Reader and io.Writer in Port<commit_after>// Package eport provides a simple API to write Erlang ports in Go.
//
// For more information on Erlang ports see http://www.erlang.org/doc/tutorial/c_port.html.
package eport

import (
	"errors"
	"io"
)

// Port is an abstraction over different types of ports.
//
// While using Read() on a line (or packet) based port it may happen that the size
// of the line (packet) is bigger than len(p). In this case eport skips the line (packet)
// and return n=0 and err=ErrTooBig.
// It is up to the caller to choose whether to consider this as a fatal error
// or to continue reading.
type Port interface {
	io.Reader
	io.Writer
	// ReadOne reads either one packet, one line (ending with '\n') or a byte
	// from a packet, line or stream-based port accordingly.
	ReadOne() (data []byte, err error)
}

var (
	ErrBadSizeLen   = errors.New("eport: bad 'packet size' length")
	ErrSizeOverflow = errors.New("eport: packet size overflow")
	ErrTooBig       = errors.New("eport: packet does not fit the buffer")
)
<|endoftext|>


<commit_before>package discordgo

// EventHandler is an interface for Discord events.
type EventHandler interface {
	// Type returns the type of event this handler belongs to.
	Type() string

	// Handle is called whenever an event of Type() happens.
	// It is the receivers responsibility to type assert that the interface
	// is the expected struct.
	Handle(*Session, interface{})
}

// EventInterfaceProvider is an interface for providing empty interfaces for
// Discord events.
type EventInterfaceProvider interface {
	// Type is the type of event this handler belongs to.
	Type() string

	// New returns a new instance of the struct this event handler handles.
	// This is called once per event.
	// The struct is provided to all handlers of the same Type().
	New() interface{}
}

// interfaceEventType is the event handler type for interface{} events.
const interfaceEventType = "__INTERFACE__"

// interfaceEventHandler is an event handler for interface{} events.
type interfaceEventHandler func(*Session, interface{})

// Type returns the event type for interface{} events.
func (eh interfaceEventHandler) Type() string {
	return interfaceEventType
}

// Handle is the handler for an interface{} event.
func (eh interfaceEventHandler) Handle(s *Session, i interface{}) {
	eh(s, i)
}

var registeredInterfaceProviders = map[string]EventInterfaceProvider{}

// registerInterfaceProvider registers a provider so that DiscordGo can
// access it's New() method.
func registerInterfaceProvider(eh EventInterfaceProvider) {
	if _, ok := registeredInterfaceProviders[eh.Type()]; ok {
		return
		// XXX:
		// if we should error here, we need to do something with it.
		// fmt.Errorf("event %s already registered", eh.Type())
	}
	registeredInterfaceProviders[eh.Type()] = eh
	return
}

// eventHandlerInstance is a wrapper around an event handler, as functions
// cannot be compared directly.
type eventHandlerInstance struct {
	eventHandler EventHandler
}

// addEventHandler adds an event handler that will be fired anytime
// the Discord WSAPI matching eventHandler.Type() fires.
func (s *Session) addEventHandler(eventHandler EventHandler) func() {
	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	if s.handlers == nil {
		s.handlers = map[string][]*eventHandlerInstance{}
	}

	ehi := &eventHandlerInstance{eventHandler}
	s.handlers[eventHandler.Type()] = append(s.handlers[eventHandler.Type()], ehi)

	return func() {
		s.removeEventHandlerInstance(eventHandler.Type(), ehi)
	}
}

// addEventHandler adds an event handler that will be fired the next time
// the Discord WSAPI matching eventHandler.Type() fires.
func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	if s.onceHandlers == nil {
		s.onceHandlers = map[string][]*eventHandlerInstance{}
	}

	ehi := &eventHandlerInstance{eventHandler}
	s.onceHandlers[eventHandler.Type()] = append(s.onceHandlers[eventHandler.Type()], ehi)

	return func() {
		s.removeEventHandlerInstance(eventHandler.Type(), ehi)
	}
}

// AddHandler allows you to add an event handler that will be fired anytime
// the Discord WSAPI event that matches the function fires.
// The first parameter is a *Session, and the second parameter is a pointer
// to a struct corresponding to the event for which you want to listen.
//
// eg:
//     Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
//     })
//
// or:
//     Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
//     })
//
// List of events can be found at this page, with corresponding names in the
// library for each event: https://discordapp.com/developers/docs/topics/gateway#event-names
// There are also synthetic events fired by the library internally which are
// available for handling, like Connect, Disconnect, and RateLimit.
// events.go contains all of the Discord WSAPI and synthetic events that can be handled.
//
// The return value of this method is a function, that when called will remove the
// event handler.
func (s *Session) AddHandler(handler interface{}) func() {
	eh := handlerForInterface(handler)

	if eh == nil {
		s.log(LogError, "Invalid handler type, handler will never be called")
		return func() {}
	}

	return s.addEventHandler(eh)
}

// AddHandlerOnce allows you to add an event handler that will be fired the next time
// the Discord WSAPI event that matches the function fires.
// See AddHandler for more details.
func (s *Session) AddHandlerOnce(handler interface{}) func() {
	eh := handlerForInterface(handler)

	if eh == nil {
		s.log(LogError, "Invalid handler type, handler will never be called")
		return func() {}
	}

	return s.addEventHandlerOnce(eh)
}

// removeEventHandler instance removes an event handler instance.
func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance) {
	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	handlers := s.handlers[t]
	for i := range handlers {
		if handlers[i] == ehi {
			s.handlers[t] = append(handlers[:i], handlers[i+1:]...)
		}
	}

	onceHandlers := s.onceHandlers[t]
	for i := range onceHandlers {
		if onceHandlers[i] == ehi {
			s.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...)
		}
	}
}

// Handles calling permanent and once handlers for an event type.
func (s *Session) handle(t string, i interface{}) {
	for _, eh := range s.handlers[t] {
		if s.SyncEvents {
			eh.eventHandler.Handle(s, i)
		} else {
			go eh.eventHandler.Handle(s, i)
		}
	}

	if len(s.onceHandlers[t]) > 0 {
		for _, eh := range s.onceHandlers[t] {
			if s.SyncEvents {
				eh.eventHandler.Handle(s, i)
			} else {
				go eh.eventHandler.Handle(s, i)
			}
		}
		s.onceHandlers[t] = nil
	}
}

// Handles an event type by calling internal methods, firing handlers and firing the
// interface{} event.
func (s *Session) handleEvent(t string, i interface{}) {
	s.handlersMu.RLock()
	defer s.handlersMu.RUnlock()

	// All events are dispatched internally first.
	s.onInterface(i)

	// Then they are dispatched to anyone handling interface{} events.
	s.handle(interfaceEventType, i)

	// Finally they are dispatched to any typed handlers.
	s.handle(t, i)
}

// setGuildIds will set the GuildID on all the members of a guild.
// This is done as event data does not have it set.
func setGuildIds(g *Guild) {
	for _, c := range g.Channels {
		c.GuildID = g.ID
	}

	for _, m := range g.Members {
		m.GuildID = g.ID
	}

	for _, vs := range g.VoiceStates {
		vs.GuildID = g.ID
	}
}

// onInterface handles all internal events and routes them to the appropriate internal handler.
func (s *Session) onInterface(i interface{}) {
	switch t := i.(type) {
	case *Ready:
		for _, g := range t.Guilds {
			setGuildIds(g)
		}
	case *GuildCreate:
		setGuildIds(t.Guild)
	case *GuildUpdate:
		setGuildIds(t.Guild)
	case *VoiceServerUpdate:
		go s.GatewayManager.onVoiceServerUpdate(t)
	case *VoiceStateUpdate:
		go s.GatewayManager.onVoiceStateUpdate(t)
	}
	err := s.State.OnInterface(s, i)
	if err != nil {
		s.log(LogDebug, "error dispatching internal event, %s", err)
	}
}
<commit_msg>added exported method to get even interface from type<commit_after>package discordgo

// EventHandler is an interface for Discord events.
type EventHandler interface {
	// Type returns the type of event this handler belongs to.
	Type() string

	// Handle is called whenever an event of Type() happens.
	// It is the receivers responsibility to type assert that the interface
	// is the expected struct.
	Handle(*Session, interface{})
}

// EventInterfaceProvider is an interface for providing empty interfaces for
// Discord events.
type EventInterfaceProvider interface {
	// Type is the type of event this handler belongs to.
	Type() string

	// New returns a new instance of the struct this event handler handles.
	// This is called once per event.
	// The struct is provided to all handlers of the same Type().
	New() interface{}
}

// interfaceEventType is the event handler type for interface{} events.
const interfaceEventType = "__INTERFACE__"

// interfaceEventHandler is an event handler for interface{} events.
type interfaceEventHandler func(*Session, interface{})

// Type returns the event type for interface{} events.
func (eh interfaceEventHandler) Type() string {
	return interfaceEventType
}

// Handle is the handler for an interface{} event.
func (eh interfaceEventHandler) Handle(s *Session, i interface{}) {
	eh(s, i)
}

var registeredInterfaceProviders = map[string]EventInterfaceProvider{}

// registerInterfaceProvider registers a provider so that DiscordGo can
// access it's New() method.
func registerInterfaceProvider(eh EventInterfaceProvider) {
	if _, ok := registeredInterfaceProviders[eh.Type()]; ok {
		return
		// XXX:
		// if we should error here, we need to do something with it.
		// fmt.Errorf("event %s already registered", eh.Type())
	}
	registeredInterfaceProviders[eh.Type()] = eh
	return
}

func GetEventInterface(evtType string) interface{} {
	if provider, ok := registeredInterfaceProviders[evtType]; ok {
		return provider.New()
	}

	return nil
}

// eventHandlerInstance is a wrapper around an event handler, as functions
// cannot be compared directly.
type eventHandlerInstance struct {
	eventHandler EventHandler
}

// addEventHandler adds an event handler that will be fired anytime
// the Discord WSAPI matching eventHandler.Type() fires.
func (s *Session) addEventHandler(eventHandler EventHandler) func() {
	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	if s.handlers == nil {
		s.handlers = map[string][]*eventHandlerInstance{}
	}

	ehi := &eventHandlerInstance{eventHandler}
	s.handlers[eventHandler.Type()] = append(s.handlers[eventHandler.Type()], ehi)

	return func() {
		s.removeEventHandlerInstance(eventHandler.Type(), ehi)
	}
}

// addEventHandler adds an event handler that will be fired the next time
// the Discord WSAPI matching eventHandler.Type() fires.
func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	if s.onceHandlers == nil {
		s.onceHandlers = map[string][]*eventHandlerInstance{}
	}

	ehi := &eventHandlerInstance{eventHandler}
	s.onceHandlers[eventHandler.Type()] = append(s.onceHandlers[eventHandler.Type()], ehi)

	return func() {
		s.removeEventHandlerInstance(eventHandler.Type(), ehi)
	}
}

// AddHandler allows you to add an event handler that will be fired anytime
// the Discord WSAPI event that matches the function fires.
// The first parameter is a *Session, and the second parameter is a pointer
// to a struct corresponding to the event for which you want to listen.
//
// eg:
//     Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
//     })
//
// or:
//     Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
//     })
//
// List of events can be found at this page, with corresponding names in the
// library for each event: https://discordapp.com/developers/docs/topics/gateway#event-names
// There are also synthetic events fired by the library internally which are
// available for handling, like Connect, Disconnect, and RateLimit.
// events.go contains all of the Discord WSAPI and synthetic events that can be handled.
//
// The return value of this method is a function, that when called will remove the
// event handler.
func (s *Session) AddHandler(handler interface{}) func() {
	eh := handlerForInterface(handler)

	if eh == nil {
		s.log(LogError, "Invalid handler type, handler will never be called")
		return func() {}
	}

	return s.addEventHandler(eh)
}

// AddHandlerOnce allows you to add an event handler that will be fired the next time
// the Discord WSAPI event that matches the function fires.
// See AddHandler for more details.
func (s *Session) AddHandlerOnce(handler interface{}) func() {
	eh := handlerForInterface(handler)

	if eh == nil {
		s.log(LogError, "Invalid handler type, handler will never be called")
		return func() {}
	}

	return s.addEventHandlerOnce(eh)
}

// removeEventHandler instance removes an event handler instance.
func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance) {
	s.handlersMu.Lock()
	defer s.handlersMu.Unlock()

	handlers := s.handlers[t]
	for i := range handlers {
		if handlers[i] == ehi {
			s.handlers[t] = append(handlers[:i], handlers[i+1:]...)
		}
	}

	onceHandlers := s.onceHandlers[t]
	for i := range onceHandlers {
		if onceHandlers[i] == ehi {
			s.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...)
		}
	}
}

// Handles calling permanent and once handlers for an event type.
func (s *Session) handle(t string, i interface{}) {
	for _, eh := range s.handlers[t] {
		if s.SyncEvents {
			eh.eventHandler.Handle(s, i)
		} else {
			go eh.eventHandler.Handle(s, i)
		}
	}

	if len(s.onceHandlers[t]) > 0 {
		for _, eh := range s.onceHandlers[t] {
			if s.SyncEvents {
				eh.eventHandler.Handle(s, i)
			} else {
				go eh.eventHandler.Handle(s, i)
			}
		}
		s.onceHandlers[t] = nil
	}
}

// Handles an event type by calling internal methods, firing handlers and firing the
// interface{} event.
func (s *Session) handleEvent(t string, i interface{}) {
	s.handlersMu.RLock()
	defer s.handlersMu.RUnlock()

	// All events are dispatched internally first.
	s.onInterface(i)

	// Then they are dispatched to anyone handling interface{} events.
	s.handle(interfaceEventType, i)

	// Finally they are dispatched to any typed handlers.
	s.handle(t, i)
}

// setGuildIds will set the GuildID on all the members of a guild.
// This is done as event data does not have it set.
func setGuildIds(g *Guild) {
	for _, c := range g.Channels {
		c.GuildID = g.ID
	}

	for _, m := range g.Members {
		m.GuildID = g.ID
	}

	for _, vs := range g.VoiceStates {
		vs.GuildID = g.ID
	}
}

// onInterface handles all internal events and routes them to the appropriate internal handler.
func (s *Session) onInterface(i interface{}) {
	switch t := i.(type) {
	case *Ready:
		for _, g := range t.Guilds {
			setGuildIds(g)
		}
	case *GuildCreate:
		setGuildIds(t.Guild)
	case *GuildUpdate:
		setGuildIds(t.Guild)
	case *VoiceServerUpdate:
		go s.GatewayManager.onVoiceServerUpdate(t)
	case *VoiceStateUpdate:
		go s.GatewayManager.onVoiceStateUpdate(t)
	}
	err := s.State.OnInterface(s, i)
	if err != nil {
		s.log(LogDebug, "error dispatching internal event, %s", err)
	}
}
<|endoftext|>


<commit_before>package gogame

/*
#cgo pkg-config: sdl2
#include "SDL.h"

int getEventType(SDL_Event *e) {
    return e->type;
}

int getKeyCode(SDL_Event *e) {
    return e->key.keysym.sym;
}

int isKeyRepeat(SDL_Event *e) {
    return e->key.repeat;
}

int isKeyPressed(int kcode) {
    const Uint8 * state = SDL_GetKeyboardState(NULL);
    int sc = SDL_GetScancodeFromKey(kcode);
    return state[sc];
}

int getWindowEvent(SDL_Event *e) {
    return e->window.event;
}

void getWinData(SDL_Event *e, int *w, int *h) {
    *w = e->window.data1;
    *h = e->window.data2;
}

void getMouseCoords(SDL_Event *e, int *x, int *y, int *down) {
    *x = e->button.x;
    *y = e->button.y;
    if (e->button.state == SDL_PRESSED) {
        *down = 1;
    } else {
        *down = 0;
    }
}

void getMouseWheel(SDL_Event *e, int *x, int *y) {
    *x = e->wheel.x;
    *y = e->wheel.y;
}

*/
import "C"

const (
	K_LEFT   = C.SDLK_LEFT
	K_RIGHT  = C.SDLK_RIGHT
	K_UP     = C.SDLK_UP
	K_DOWN   = C.SDLK_DOWN
	K_SPACE  = C.SDLK_SPACE
	K_ESC    = C.SDLK_ESCAPE
	K_RETURN = C.SDLK_RETURN
	K_P      = C.SDLK_p
	K_I      = C.SDLK_i
	K_M      = C.SDLK_m
	K_Q      = C.SDLK_q
	K_F      = C.SDLK_f
	K_T      = C.SDLK_t
)

type Event interface{}

type EventQuit interface{}

type EventUnknown interface{}

type EventExposed interface{}

type EventMouseClick struct {
	X, Y int
	Down bool
}

type EventMouseWheel struct {
	X, Y int
}

type EventKey struct {
	Code int
	Down bool
}

type EventResize struct {
	W, H int
}

// Wait for next event
func WaitEvent() Event {
	var cev C.SDL_Event
	if 0 == C.SDL_WaitEvent(&cev) {
		return nil
	}
	return classifyEvent(&cev)
}

func WaitEventTimeout(timeout int) Event {
	var cev C.SDL_Event
	if 0 == C.SDL_WaitEventTimeout(&cev, C.int(timeout)) {
		return nil
	}
	return classifyEvent(&cev)
}

// Poll for pending envents. Return nil if there is no event available
func PollEvent() Event {
	var cev C.SDL_Event
	if 0 == C.SDL_PollEvent(&cev) {
		return nil
	}
	return classifyEvent(&cev)
}

func classifyEvent(cev *C.SDL_Event) Event {
	switch C.getEventType(cev) {

	case C.SDL_QUIT:
		return new(EventQuit)

	case C.SDL_KEYDOWN:
		// Ignore repeat key events
		if C.isKeyRepeat(cev) != 0 {
			break
		}
		kde := new(EventKey)
		kde.Code = int(C.getKeyCode(cev))
		kde.Down = true
		return kde

	case C.SDL_KEYUP:
		kde := new(EventKey)
		kde.Code = int(C.getKeyCode(cev))
		kde.Down = false
		return kde

	case C.SDL_WINDOWEVENT:
		wet := C.getWindowEvent(cev)
		if wet == C.SDL_WINDOWEVENT_RESIZED {
			var w, h C.int
			C.getWinData(cev, &w, &h)
			wr := new(EventResize)
			wr.W = int(w)
			wr.H = int(h)
			return wr

		} else if wet == C.SDL_WINDOWEVENT_EXPOSED {
			return new(EventExposed)
		}

	case C.SDL_MOUSEBUTTONDOWN:
		var x, y, dw C.int
		var down bool
		C.getMouseCoords(cev, &x, &y, &dw)
		if dw == 1 {
			down = true
		}
		return &EventMouseClick{X: int(x), Y: int(y), Down: down}

	case C.SDL_MOUSEWHEEL:
		var x, y C.int
		C.getMouseWheel(cev, &x, &y)
		return &EventMouseWheel{int(x), int(y)}
	}
	return new(EventUnknown)
}

// Process events. Returns true if EventQuit has appeared
func SlurpEvents() (quit bool) {
	quit = false
	for {
		ev := PollEvent()
		if ev == nil {
			return
		}
		switch ev.(type) {
		case *EventQuit:
			quit = true
		}
	}
}

// Returns true if key is pressed, false otherwise
func IsKeyPressed(kcode int) bool {
	return C.isKeyPressed(C.int(kcode)) == 1
}
<commit_msg>Changed key codes for scancodes<commit_after>package gogame

/*
#cgo pkg-config: sdl2
#include "SDL.h"

int getEventType(SDL_Event *e) {
    return e->type;
}

int getKeyCode(SDL_Event *e) {
    return e->key.keysym.scancode;
}

int isKeyRepeat(SDL_Event *e) {
    return e->key.repeat;
}

int isKeyPressed(int kcode) {
    const Uint8 * state = SDL_GetKeyboardState(NULL);
    return state[kcode];
    // int sc = SDL_GetScancodeFromKey(kcode);
    // return state[sc];
}

int getWindowEvent(SDL_Event *e) {
    return e->window.event;
}

void getWinData(SDL_Event *e, int *w, int *h) {
    *w = e->window.data1;
    *h = e->window.data2;
}

void getMouseCoords(SDL_Event *e, int *x, int *y, int *down) {
    *x = e->button.x;
    *y = e->button.y;
    if (e->button.state == SDL_PRESSED) {
        *down = 1;
    } else {
        *down = 0;
    }
}

void getMouseWheel(SDL_Event *e, int *x, int *y) {
    *x = e->wheel.x;
    *y = e->wheel.y;
}

*/
import "C"

const (
	K_LEFT   = C.SDL_SCANCODE_LEFT
	K_RIGHT  = C.SDL_SCANCODE_RIGHT
	K_UP     = C.SDL_SCANCODE_UP
	K_DOWN   = C.SDL_SCANCODE_DOWN
	K_SPACE  = C.SDL_SCANCODE_SPACE
	K_ESC    = C.SDL_SCANCODE_ESCAPE
	K_RETURN = C.SDL_SCANCODE_RETURN
	K_A      = C.SDL_SCANCODE_A
	K_B      = C.SDL_SCANCODE_B
	K_C      = C.SDL_SCANCODE_C
	K_D      = C.SDL_SCANCODE_D
	K_E      = C.SDL_SCANCODE_E
	K_F      = C.SDL_SCANCODE_F
	K_G      = C.SDL_SCANCODE_G
	K_H      = C.SDL_SCANCODE_H
	K_I      = C.SDL_SCANCODE_I
	K_J      = C.SDL_SCANCODE_J
	K_K      = C.SDL_SCANCODE_K
	K_L      = C.SDL_SCANCODE_L
	K_M      = C.SDL_SCANCODE_M
	K_N      = C.SDL_SCANCODE_N
	K_O      = C.SDL_SCANCODE_O
	K_P      = C.SDL_SCANCODE_P
	K_Q      = C.SDL_SCANCODE_Q
	K_R      = C.SDL_SCANCODE_R
	K_S      = C.SDL_SCANCODE_S
	K_T      = C.SDL_SCANCODE_T
	K_U      = C.SDL_SCANCODE_U
	K_V      = C.SDL_SCANCODE_V
	K_W      = C.SDL_SCANCODE_W
	K_X      = C.SDL_SCANCODE_X
	K_Y      = C.SDL_SCANCODE_Y
	K_Z      = C.SDL_SCANCODE_Z
)

type Event interface{}

type EventQuit interface{}

type EventUnknown interface{}

type EventExposed interface{}

type EventMouseClick struct {
	X, Y int
	Down bool
}

type EventMouseWheel struct {
	X, Y int
}

type EventKey struct {
	Code int
	Down bool
}

type EventResize struct {
	W, H int
}

// Wait for next event
func WaitEvent() Event {
	var cev C.SDL_Event
	if 0 == C.SDL_WaitEvent(&cev) {
		return nil
	}
	return classifyEvent(&cev)
}

func WaitEventTimeout(timeout int) Event {
	var cev C.SDL_Event
	if 0 == C.SDL_WaitEventTimeout(&cev, C.int(timeout)) {
		return nil
	}
	return classifyEvent(&cev)
}

// Poll for pending envents. Return nil if there is no event available
func PollEvent() Event {
	var cev C.SDL_Event
	if 0 == C.SDL_PollEvent(&cev) {
		return nil
	}
	return classifyEvent(&cev)
}

func classifyEvent(cev *C.SDL_Event) Event {
	switch C.getEventType(cev) {

	case C.SDL_QUIT:
		return new(EventQuit)

	case C.SDL_KEYDOWN:
		// Ignore repeat key events
		if C.isKeyRepeat(cev) != 0 {
			break
		}
		kde := new(EventKey)
		kde.Code = int(C.getKeyCode(cev))
		kde.Down = true
		return kde

	case C.SDL_KEYUP:
		kde := new(EventKey)
		kde.Code = int(C.getKeyCode(cev))
		kde.Down = false
		return kde

	case C.SDL_WINDOWEVENT:
		wet := C.getWindowEvent(cev)
		if wet == C.SDL_WINDOWEVENT_RESIZED {
			var w, h C.int
			C.getWinData(cev, &w, &h)
			wr := new(EventResize)
			wr.W = int(w)
			wr.H = int(h)
			return wr

		} else if wet == C.SDL_WINDOWEVENT_EXPOSED {
			return new(EventExposed)
		}

	case C.SDL_MOUSEBUTTONDOWN:
		var x, y, dw C.int
		var down bool
		C.getMouseCoords(cev, &x, &y, &dw)
		if dw == 1 {
			down = true
		}
		return &EventMouseClick{X: int(x), Y: int(y), Down: down}

	case C.SDL_MOUSEWHEEL:
		var x, y C.int
		C.getMouseWheel(cev, &x, &y)
		return &EventMouseWheel{int(x), int(y)}
	}
	return new(EventUnknown)
}

// Process events. Returns true if EventQuit has appeared
func SlurpEvents() (quit bool) {
	quit = false
	for {
		ev := PollEvent()
		if ev == nil {
			return
		}
		switch ev.(type) {
		case *EventQuit:
			quit = true
		}
	}
}

// Returns true if key is pressed, false otherwise
func IsKeyPressed(kcode int) bool {
	return C.isKeyPressed(C.int(kcode)) == 1
}
<|endoftext|>


<commit_before>package cl11

import (
	"fmt"
	"unsafe"

	clw "github.com/rdwilliamson/clw11"
)

// Event objects can be used to refer to a kernel execution command, read,
// write, map and copy commands on memory objects, EnqueueMarker, or user
// events.
//
// An event object can be used to track the execution status of a command. The
// API calls that enqueue commands to a command-queue create a new event object
// that is returned in the event argument.
type Event struct {
	id           clw.Event
	Context      *Context
	CommandType  CommandType
	CommandQueue *CommandQueue

	Queued int64 // Profiling information.
	Submit int64 // Profiling information.
	Start  int64 // Profiling information.
	End    int64 // Profiling information.
}

type CommandType int

const (
	CommandNDRangeKernel        = CommandType(clw.CommandNdrangeKernel)
	CommandTask                 = CommandType(clw.CommandTask)
	CommandNativeKernel         = CommandType(clw.CommandNativeKernel)
	CommandReadBuffer           = CommandType(clw.CommandReadBuffer)
	CommandWriteBuffer          = CommandType(clw.CommandWriteBuffer)
	CommandCopyBuffer           = CommandType(clw.CommandCopyBuffer)
	CommandReadImage            = CommandType(clw.CommandReadImage)
	CommandWriteImage           = CommandType(clw.CommandWriteImage)
	CommandCopyImage            = CommandType(clw.CommandCopyImage)
	CommandCopyImageToBuffer    = CommandType(clw.CommandCopyImageToBuffer)
	CommandCopyBufferToImage    = CommandType(clw.CommandCopyBufferToImage)
	CommandMapBuffer            = CommandType(clw.CommandMapBuffer)
	CommandMapImage             = CommandType(clw.CommandMapImage)
	CommandUnmapMemoryObject    = CommandType(clw.CommandUnmapMemoryObject)
	CommandMarker               = CommandType(clw.CommandMarker)
	CommandAcquireGlObjects     = CommandType(clw.CommandAcquireGlObjects)
	CommandReleaseGlObjects     = CommandType(clw.CommandReleaseGlObjects)
	CommandReadBufferRectangle  = CommandType(clw.CommandReadBufferRectangle)
	CommandWriteBufferRectangle = CommandType(clw.CommandWriteBufferRectangle)
	CommandCopyBufferRectangle  = CommandType(clw.CommandCopyBufferRectangle)
	CommandUser                 = CommandType(clw.CommandUser)
)

var commandTypeMap = map[CommandType]string{
	CommandNDRangeKernel:        "ND range kernel",
	CommandTask:                 "task",
	CommandNativeKernel:         "native kernel",
	CommandReadBuffer:           "read buffer",
	CommandWriteBuffer:          "write buffer",
	CommandCopyBuffer:           "copy buffer",
	CommandReadImage:            "read image",
	CommandWriteImage:           "write image",
	CommandCopyImage:            "copy image",
	CommandCopyImageToBuffer:    "copy image to buffer",
	CommandCopyBufferToImage:    "copy buffer to image",
	CommandMapBuffer:            "map buffer",
	CommandMapImage:             "map image",
	CommandUnmapMemoryObject:    "unmap memory object",
	CommandMarker:               "marker",
	CommandAcquireGlObjects:     "acquire GL objects",
	CommandReleaseGlObjects:     "release GL objects",
	CommandReadBufferRectangle:  "read buffer rectangle",
	CommandWriteBufferRectangle: "write buffer rectangle",
	CommandCopyBufferRectangle:  "copy buffer rectangle",
	CommandUser:                 "user",
}

func (ct CommandType) String() string {
	return commandTypeMap[ct]
}

type CommandExecutionStatus int

const (
	Queued    = CommandExecutionStatus(clw.Queued)
	Submitted = CommandExecutionStatus(clw.Submitted)
	Running   = CommandExecutionStatus(clw.Running)
	Complete  = CommandExecutionStatus(clw.Complete)
)

func (ces CommandExecutionStatus) String() string {
	switch ces {
	case Complete:
		return "complete"
	case Running:
		return "running"
	case Submitted:
		return "submitted"
	case Queued:
		return "queued"
	}
	return ""
}

// Creates a user event object.
//
// User events allow applications to enqueue commands that wait on a user event
// to finish before the command is executed by the device. The execution status
// of the user event object created is set to Submitted.
func (c *Context) CreateUserEvent() (*Event, error) {

	event, err := clw.CreateUserEvent(c.id)
	if err != nil {
		return nil, err
	}

	return &Event{id: event, Context: c, CommandType: CommandUser}, nil
}

// Return the execution status of the command identified by event.
//
// Statuses are: Queued (command has been enqueued in the command-queue),
// Submitted (enqueued command has been submitted by the host to the device
// associated with the command-queue), Running (device is currently executing
// this command), Complete (the command has completed).
//
// An eventErr is an error that cause the event to abnormally terminate. A
// getStatusErr is an error that occurred while trying to retrieve the event's
// status.
func (e *Event) Status() (ces CommandExecutionStatus, eventErr, getStatusErr error) {

	err := clw.GetEventInfo(e.id, clw.EventCommandExecutionStatus, clw.Size(unsafe.Sizeof(ces)),
		unsafe.Pointer(&ces), nil)
	if err != nil {
		return 0, nil, err
	}

	if ces < 0 {
		return 0, fmt.Errorf("event abnormally terminated: %s", clw.CodeToError(clw.Int(ces)).Error()), nil
	}

	return CommandExecutionStatus(ces), nil, nil
}

// Gets profiling information for the command associated with event if profiling
// is enabled.
//
// The 64-bit values can be used to measure the time in nano-seconds consumed by
// OpenCL commands.
//
// OpenCL devices are required to correctly track time across changes in device
// frequency and power states. The Device.ProfilingTimerResolution specifies the
// resolution of the timer i.e. the number of nanoseconds elapsed before the
// timer is incremented.
//
// Event objects can be used to capture profiling information that measure
// execution time of a command. Profiling of OpenCL commands can be enabled
// either by using a command-queue created with QueueProfilingEnable flag set in
// properties argument to CreateCommandQueue.
func (e *Event) GetProfilingInfo() error {

	var value clw.Ulong
	err := clw.GetEventProfilingInfo(e.id, clw.ProfilingCommandQueued, clw.Size(unsafe.Sizeof(value)),
		unsafe.Pointer(&value), nil)
	if err != nil {
		return err
	}
	e.Queued = int64(value)

	err = clw.GetEventProfilingInfo(e.id, clw.ProfilingCommandSubmit, clw.Size(unsafe.Sizeof(value)),
		unsafe.Pointer(&value), nil)
	if err != nil {
		return err
	}
	e.Submit = int64(value)

	err = clw.GetEventProfilingInfo(e.id, clw.ProfilingCommandStart, clw.Size(unsafe.Sizeof(value)),
		unsafe.Pointer(&value), nil)
	if err != nil {
		return err
	}
	e.Start = int64(value)

	err = clw.GetEventProfilingInfo(e.id, clw.ProfilingCommandEnd, clw.Size(unsafe.Sizeof(value)),
		unsafe.Pointer(&value), nil)
	if err != nil {
		return err
	}
	e.End = int64(value)

	return nil
}

// Registers a user callback function for a specific command execution status.
//
// The registered callback function will be called when the execution status of
// command associated with event changes to Complete.
//
// Each call to SetCallback registers the specified user callback
// function on a callback stack associated with event. The order in which the
// registered user callback functions are called is undefined.
//
// All callbacks registered for an event object must be called. All enqueued
// callbacks shall be called before the event object is destroyed. Callbacks
// must return promptly. The behavior of calling expensive system routines,
// OpenCL API calls to create contexts or command-queues, or blocking OpenCL
// operations, in a callback is undefined.
func (e *Event) SetCallback(callback func(e *Event, userData interface{}), userData interface{}) error {

	return clw.SetEventCallback(e.id, clw.Complete,
		func(event clw.Event, ces clw.CommandExecutionStatus, _userData interface{}) {
			callback(e, _userData)
		},
		userData)
}

// Waits on the host thread for the event to complete. See WaitForEvents for
// more info.
func (e *Event) Wait() error {
	return clw.WaitForEvents([]clw.Event{e.id})
}

// Waits on the host thread for commands identified by event objects to
// complete.
//
// Waits on the host thread for commands identified by event objects in
// event_list to complete. A command is considered complete if its execution
// status is "complete" or abnormally terminated (an error occured).
//
// If the cl_khr_gl_event extension is enabled, event objects can also be used
// to reflect the status of an OpenGL sync object. The sync object in turn
// refers to a fence command executing in an OpenGL command stream. This
// provides another method of coordinating sharing of buffers and images between
// OpenGL and OpenCL.
func WaitForEvents(events ...*Event) error {
	waitList := make([]clw.Event, len(events))
	for i := range events {
		waitList[i] = events[i].id
	}
	return clw.WaitForEvents(waitList)
}
<commit_msg>Added retaining, releasing, and reference count for events.<commit_after>package cl11

import (
	"fmt"
	"unsafe"

	clw "github.com/rdwilliamson/clw11"
)

// Event objects can be used to refer to a kernel execution command, read,
// write, map and copy commands on memory objects, EnqueueMarker, or user
// events.
//
// An event object can be used to track the execution status of a command. The
// API calls that enqueue commands to a command-queue create a new event object
// that is returned in the event argument.
type Event struct {
	id           clw.Event
	Context      *Context
	CommandType  CommandType
	CommandQueue *CommandQueue

	Queued int64 // Profiling information.
	Submit int64 // Profiling information.
	Start  int64 // Profiling information.
	End    int64 // Profiling information.
}

type CommandType int

const (
	CommandNDRangeKernel        = CommandType(clw.CommandNdrangeKernel)
	CommandTask                 = CommandType(clw.CommandTask)
	CommandNativeKernel         = CommandType(clw.CommandNativeKernel)
	CommandReadBuffer           = CommandType(clw.CommandReadBuffer)
	CommandWriteBuffer          = CommandType(clw.CommandWriteBuffer)
	CommandCopyBuffer           = CommandType(clw.CommandCopyBuffer)
	CommandReadImage            = CommandType(clw.CommandReadImage)
	CommandWriteImage           = CommandType(clw.CommandWriteImage)
	CommandCopyImage            = CommandType(clw.CommandCopyImage)
	CommandCopyImageToBuffer    = CommandType(clw.CommandCopyImageToBuffer)
	CommandCopyBufferToImage    = CommandType(clw.CommandCopyBufferToImage)
	CommandMapBuffer            = CommandType(clw.CommandMapBuffer)
	CommandMapImage             = CommandType(clw.CommandMapImage)
	CommandUnmapMemoryObject    = CommandType(clw.CommandUnmapMemoryObject)
	CommandMarker               = CommandType(clw.CommandMarker)
	CommandAcquireGlObjects     = CommandType(clw.CommandAcquireGlObjects)
	CommandReleaseGlObjects     = CommandType(clw.CommandReleaseGlObjects)
	CommandReadBufferRectangle  = CommandType(clw.CommandReadBufferRectangle)
	CommandWriteBufferRectangle = CommandType(clw.CommandWriteBufferRectangle)
	CommandCopyBufferRectangle  = CommandType(clw.CommandCopyBufferRectangle)
	CommandUser                 = CommandType(clw.CommandUser)
)

var commandTypeMap = map[CommandType]string{
	CommandNDRangeKernel:        "ND range kernel",
	CommandTask:                 "task",
	CommandNativeKernel:         "native kernel",
	CommandReadBuffer:           "read buffer",
	CommandWriteBuffer:          "write buffer",
	CommandCopyBuffer:           "copy buffer",
	CommandReadImage:            "read image",
	CommandWriteImage:           "write image",
	CommandCopyImage:            "copy image",
	CommandCopyImageToBuffer:    "copy image to buffer",
	CommandCopyBufferToImage:    "copy buffer to image",
	CommandMapBuffer:            "map buffer",
	CommandMapImage:             "map image",
	CommandUnmapMemoryObject:    "unmap memory object",
	CommandMarker:               "marker",
	CommandAcquireGlObjects:     "acquire GL objects",
	CommandReleaseGlObjects:     "release GL objects",
	CommandReadBufferRectangle:  "read buffer rectangle",
	CommandWriteBufferRectangle: "write buffer rectangle",
	CommandCopyBufferRectangle:  "copy buffer rectangle",
	CommandUser:                 "user",
}

func (ct CommandType) String() string {
	return commandTypeMap[ct]
}

type CommandExecutionStatus int

const (
	Queued    = CommandExecutionStatus(clw.Queued)
	Submitted = CommandExecutionStatus(clw.Submitted)
	Running   = CommandExecutionStatus(clw.Running)
	Complete  = CommandExecutionStatus(clw.Complete)
)

func (ces CommandExecutionStatus) String() string {
	switch ces {
	case Complete:
		return "complete"
	case Running:
		return "running"
	case Submitted:
		return "submitted"
	case Queued:
		return "queued"
	}
	return ""
}

// Creates a user event object.
//
// User events allow applications to enqueue commands that wait on a user event
// to finish before the command is executed by the device. The execution status
// of the user event object created is set to Submitted.
func (c *Context) CreateUserEvent() (*Event, error) {

	event, err := clw.CreateUserEvent(c.id)
	if err != nil {
		return nil, err
	}

	return &Event{id: event, Context: c, CommandType: CommandUser}, nil
}

// Return the execution status of the command identified by event.
//
// Statuses are: Queued (command has been enqueued in the command-queue),
// Submitted (enqueued command has been submitted by the host to the device
// associated with the command-queue), Running (device is currently executing
// this command), Complete (the command has completed).
//
// An eventErr is an error that cause the event to abnormally terminate. A
// getStatusErr is an error that occurred while trying to retrieve the event's
// status.
func (e *Event) Status() (ces CommandExecutionStatus, eventErr, getStatusErr error) {

	err := clw.GetEventInfo(e.id, clw.EventCommandExecutionStatus, clw.Size(unsafe.Sizeof(ces)),
		unsafe.Pointer(&ces), nil)
	if err != nil {
		return 0, nil, err
	}

	if ces < 0 {
		return 0, fmt.Errorf("event abnormally terminated: %s", clw.CodeToError(clw.Int(ces)).Error()), nil
	}

	return CommandExecutionStatus(ces), nil, nil
}

// Gets profiling information for the command associated with event if profiling
// is enabled.
//
// The 64-bit values can be used to measure the time in nano-seconds consumed by
// OpenCL commands.
//
// OpenCL devices are required to correctly track time across changes in device
// frequency and power states. The Device.ProfilingTimerResolution specifies the
// resolution of the timer i.e. the number of nanoseconds elapsed before the
// timer is incremented.
//
// Event objects can be used to capture profiling information that measure
// execution time of a command. Profiling of OpenCL commands can be enabled
// either by using a command-queue created with QueueProfilingEnable flag set in
// properties argument to CreateCommandQueue.
func (e *Event) GetProfilingInfo() error {

	var value clw.Ulong
	err := clw.GetEventProfilingInfo(e.id, clw.ProfilingCommandQueued, clw.Size(unsafe.Sizeof(value)),
		unsafe.Pointer(&value), nil)
	if err != nil {
		return err
	}
	e.Queued = int64(value)

	err = clw.GetEventProfilingInfo(e.id, clw.ProfilingCommandSubmit, clw.Size(unsafe.Sizeof(value)),
		unsafe.Pointer(&value), nil)
	if err != nil {
		return err
	}
	e.Submit = int64(value)

	err = clw.GetEventProfilingInfo(e.id, clw.ProfilingCommandStart, clw.Size(unsafe.Sizeof(value)),
		unsafe.Pointer(&value), nil)
	if err != nil {
		return err
	}
	e.Start = int64(value)

	err = clw.GetEventProfilingInfo(e.id, clw.ProfilingCommandEnd, clw.Size(unsafe.Sizeof(value)),
		unsafe.Pointer(&value), nil)
	if err != nil {
		return err
	}
	e.End = int64(value)

	return nil
}

// Registers a user callback function for a specific command execution status.
//
// The registered callback function will be called when the execution status of
// command associated with event changes to Complete.
//
// Each call to SetCallback registers the specified user callback
// function on a callback stack associated with event. The order in which the
// registered user callback functions are called is undefined.
//
// All callbacks registered for an event object must be called. All enqueued
// callbacks shall be called before the event object is destroyed. Callbacks
// must return promptly. The behavior of calling expensive system routines,
// OpenCL API calls to create contexts or command-queues, or blocking OpenCL
// operations, in a callback is undefined.
func (e *Event) SetCallback(callback func(e *Event, userData interface{}), userData interface{}) error {

	return clw.SetEventCallback(e.id, clw.Complete,
		func(event clw.Event, ces clw.CommandExecutionStatus, _userData interface{}) {
			callback(e, _userData)
		},
		userData)
}

// Waits on the host thread for the event to complete. See WaitForEvents for
// more info.
func (e *Event) Wait() error {
	return clw.WaitForEvents([]clw.Event{e.id})
}

// Waits on the host thread for commands identified by event objects to
// complete.
//
// Waits on the host thread for commands identified by event objects in
// event_list to complete. A command is considered complete if its execution
// status is "complete" or abnormally terminated (an error occured).
//
// If the cl_khr_gl_event extension is enabled, event objects can also be used
// to reflect the status of an OpenGL sync object. The sync object in turn
// refers to a fence command executing in an OpenGL command stream. This
// provides another method of coordinating sharing of buffers and images between
// OpenGL and OpenCL.
func WaitForEvents(events ...*Event) error {
	waitList := make([]clw.Event, len(events))
	for i := range events {
		waitList[i] = events[i].id
	}
	return clw.WaitForEvents(waitList)
}

// Increments the event reference count.
//
// The OpenCL commands that return an event perform an implicit retain.
func (e *Event) Retain() error {
	return clw.RetainEvent(e.id)
}

// Decrements the event reference count.
//
// Decrements the event reference count. The event object is deleted once the
// reference count becomes zero, the specific command identified by this event
// has completed (or terminated) and there are no commands in the command-queues
// of a context that require a wait for this event to complete.
func (e *Event) Release() error {
	return clw.ReleaseEvent(e.id)
}

// The event reference count.
//
// The reference count returned should be considered immediately stale. It is
// unsuitable for general use in applications. This feature is provided for
// identifying memory leaks.
func (e *Event) ReferenceCount() (int, error) {
	var param clw.Uint
	err := clw.GetEventInfo(e.id, clw.EventReferenceCount, clw.Size(unsafe.Sizeof(param)), unsafe.Pointer(&param), nil)
	return int(param), err
}
<|endoftext|>


<commit_before>package ssgo
// Copyright 2015 tNextDay
// Copyright 2012 Gary Burd
//
// 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.

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"sync"
)

func cannotConvert(d reflect.Value, s interface{}) error {
	return fmt.Errorf("ssgo: Scan cannot convert from %s to %s",
		reflect.TypeOf(s), d.Type())
}

func convertAssignString(d reflect.Value, s string) (err error) {
	switch d.Type().Kind() {
	case reflect.Float32, reflect.Float64:
		var x float64
		x, err = strconv.ParseFloat(s, d.Type().Bits())
		d.SetFloat(x)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		var x int64
		x, err = strconv.ParseInt(s, 10, d.Type().Bits())
		d.SetInt(x)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		var x uint64
		x, err = strconv.ParseUint(s, 10, d.Type().Bits())
		d.SetUint(x)
	case reflect.Bool:
		var x bool
		x, err = strconv.ParseBool(s)
		d.SetBool(x)
	case reflect.String:
		d.SetString(s)
	case reflect.Slice:
		if d.Type().Elem().Kind() != reflect.Uint8 {
			err = cannotConvert(d, s)
		} else {
			d.SetBytes([]byte(s))
		}
	default:
		err = cannotConvert(d, s)
	}
	return
}

type fieldSpec struct {
	name      string
	index     []int
	omitEmpty bool
}

type structSpec struct {
	m map[string]*fieldSpec
	l []*fieldSpec
}

func (ss *structSpec) fieldSpec(name string) *fieldSpec {
	return ss.m[name]
}

func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)
		switch {
		case f.PkgPath != "":
		// Ignore unexported fields.
		case f.Anonymous:
			// TODO: Handle pointers. Requires change to decoder and
			// protection against infinite recursion.
			if f.Type.Kind() == reflect.Struct {
				compileStructSpec(f.Type, depth, append(index, i), ss)
			}
		default:
			fs := &fieldSpec{name: f.Name}
			tag := f.Tag.Get("ssgo")
			if tag == "" {
				//TODO don't use json tag?
				tag = f.Tag.Get("json")
			}
			p := strings.Split(tag, ",")
			if len(p) > 0 {
				if p[0] == "-" {
					continue
				}
				if len(p[0]) > 0 {
					fs.name = p[0]
				}
				for _, s := range p[1:] {
					switch s {
					case "omitempty":
						fs.omitEmpty = true
					default:
						panic(errors.New("ssgo: unknown field flag " + s + " for type " + t.Name()))
					}
				}
			}
			d, found := depth[fs.name]
			if !found {
				d = 1 << 30
			}
			switch {
			case len(index) == d:
				// At same depth, remove from result.
				delete(ss.m, fs.name)
				j := 0
				for i := 0; i < len(ss.l); i++ {
					if fs.name != ss.l[i].name {
						ss.l[j] = ss.l[i]
						j += 1
					}
				}
				ss.l = ss.l[:j]
			case len(index) < d:
				fs.index = make([]int, len(index)+1)
				copy(fs.index, index)
				fs.index[len(index)] = i
				depth[fs.name] = len(index)
				ss.m[fs.name] = fs
				ss.l = append(ss.l, fs)
			}
		}
	}
}

var (
	structSpecMutex  sync.RWMutex
	structSpecCache  = make(map[reflect.Type]*structSpec)
	defaultFieldSpec = &fieldSpec{}
)

func structSpecForType(t reflect.Type) *structSpec {

	structSpecMutex.RLock()
	ss, found := structSpecCache[t]
	structSpecMutex.RUnlock()
	if found {
		return ss
	}

	structSpecMutex.Lock()
	defer structSpecMutex.Unlock()
	ss, found = structSpecCache[t]
	if found {
		return ss
	}

	ss = &structSpec{m: make(map[string]*fieldSpec)}
	compileStructSpec(t, make(map[string]int), nil, ss)
	structSpecCache[t] = ss
	return ss
}

var errScanStructValue = errors.New("ssgo: ScanStruct value must be non-nil pointer to a struct")

// ScanStruct scans alternating names and values from src to a struct. The
// HGETALL and CONFIG GET commands return replies in this format.
//
// ScanStruct uses exported field names to match values in the response. Use
// 'ssgo' field tag to override the name:
//
//      Field int `ssgo:"myName"`
//
// Fields with the tag ssgo:"-" are ignored.
// !Note!: if field don't have ssgo: tag, ScanStruct will try to use json: tag
//
// Integer, float, boolean, string and []byte fields are supported. Scan uses the
// standard strconv package to convert bulk string values to numeric and
// boolean types.
//
// If a src element is nil, then the corresponding field is not modified.
func ScanStruct(src []string, dest interface{}) error {
	d := reflect.ValueOf(dest)
	if d.Kind() != reflect.Ptr || d.IsNil() {
		return errScanStructValue
	}
	d = d.Elem()
	if d.Kind() != reflect.Struct {
		return errScanStructValue
	}
	ss := structSpecForType(d.Type())

	if len(src)%2 != 0 {
		return errors.New("ssgo: ScanStruct expects even number of values in values")
	}

	for i := 0; i < len(src); i += 2 {
		s := src[i+1]
		if s == "" {
			continue
		}
		fs := ss.fieldSpec(src[i])
		if fs == nil {
			continue
		}
		if err := convertAssignString(d.FieldByIndex(fs.index), s); err != nil {
			return err
		}
	}
	return nil
}

// Args is a helper for constructing command arguments from structured values.
type Args []interface{}

// Add returns the result of appending value to args.
func (args Args) Add(value ...interface{}) Args {
	return append(args, value...)
}

func keyContains(s string, sl []string) bool {
	for _, v := range sl {
		if s == v {
			return true
		}
	}
	return false
}

// AddFlat returns the result of appending the flattened value of v to args.
//
// Maps are flattened by appending the alternating keys and map values to args.
//
// Slices are flattened by appending the slice elements to args.
//
// Structs are flattened by appending the alternating names and values of
// exported fields to args. If v is a nil struct pointer, then nothing is
// appended. The 'ssgo' field tag overrides struct field names. See ScanStruct
// for more information on the use of the 'ssgo' field tag.
//
// Other types are appended to args as is.
func (args Args) AddFlat(v interface{}, keys ...string) Args {
	rv := reflect.ValueOf(v)
	switch rv.Kind() {
	case reflect.Struct:
		args = flattenStruct(args, rv, keys...)
	case reflect.Slice:
		for i := 0; i < rv.Len(); i++ {
			args = append(args, rv.Index(i).Interface())
		}
	case reflect.Map:
		for _, k := range rv.MapKeys() {
			args = append(args, k.Interface(), rv.MapIndex(k).Interface())
		}
	case reflect.Ptr:
		if rv.Type().Elem().Kind() == reflect.Struct {
			if !rv.IsNil() {
				args = flattenStruct(args, rv.Elem())
			}
		} else {
			args = append(args, v)
		}
	default:
		args = append(args, v)
	}
	return args
}

func flattenStruct(args Args, v reflect.Value, keys ...string) Args {
	ss := structSpecForType(v.Type())
	l := len(keys)
	for _, fs := range ss.l {
		fv := v.FieldByIndex(fs.index)
		if l > 0 && !keyContains(fs.name, keys) {
			continue
		}
		args = append(args, fs.name, fv.Interface())
	}
	return args
}
<commit_msg>format code<commit_after>package ssgo

// Copyright 2015 tNextDay
// Copyright 2012 Gary Burd
//
// 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.

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"sync"
)

func cannotConvert(d reflect.Value, s interface{}) error {
	return fmt.Errorf("ssgo: Scan cannot convert from %s to %s",
		reflect.TypeOf(s), d.Type())
}

func convertAssignString(d reflect.Value, s string) (err error) {
	switch d.Type().Kind() {
	case reflect.Float32, reflect.Float64:
		var x float64
		x, err = strconv.ParseFloat(s, d.Type().Bits())
		d.SetFloat(x)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		var x int64
		x, err = strconv.ParseInt(s, 10, d.Type().Bits())
		d.SetInt(x)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		var x uint64
		x, err = strconv.ParseUint(s, 10, d.Type().Bits())
		d.SetUint(x)
	case reflect.Bool:
		var x bool
		x, err = strconv.ParseBool(s)
		d.SetBool(x)
	case reflect.String:
		d.SetString(s)
	case reflect.Slice:
		if d.Type().Elem().Kind() != reflect.Uint8 {
			err = cannotConvert(d, s)
		} else {
			d.SetBytes([]byte(s))
		}
	default:
		err = cannotConvert(d, s)
	}
	return
}

type fieldSpec struct {
	name      string
	index     []int
	omitEmpty bool
}

type structSpec struct {
	m map[string]*fieldSpec
	l []*fieldSpec
}

func (ss *structSpec) fieldSpec(name string) *fieldSpec {
	return ss.m[name]
}

func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)
		switch {
		case f.PkgPath != "":
		// Ignore unexported fields.
		case f.Anonymous:
			// TODO: Handle pointers. Requires change to decoder and
			// protection against infinite recursion.
			if f.Type.Kind() == reflect.Struct {
				compileStructSpec(f.Type, depth, append(index, i), ss)
			}
		default:
			fs := &fieldSpec{name: f.Name}
			tag := f.Tag.Get("ssgo")
			if tag == "" {
				//TODO don't use json tag?
				tag = f.Tag.Get("json")
			}
			p := strings.Split(tag, ",")
			if len(p) > 0 {
				if p[0] == "-" {
					continue
				}
				if len(p[0]) > 0 {
					fs.name = p[0]
				}
				for _, s := range p[1:] {
					switch s {
					case "omitempty":
						fs.omitEmpty = true
					default:
						panic(errors.New("ssgo: unknown field flag " + s + " for type " + t.Name()))
					}
				}
			}
			d, found := depth[fs.name]
			if !found {
				d = 1 << 30
			}
			switch {
			case len(index) == d:
				// At same depth, remove from result.
				delete(ss.m, fs.name)
				j := 0
				for i := 0; i < len(ss.l); i++ {
					if fs.name != ss.l[i].name {
						ss.l[j] = ss.l[i]
						j += 1
					}
				}
				ss.l = ss.l[:j]
			case len(index) < d:
				fs.index = make([]int, len(index)+1)
				copy(fs.index, index)
				fs.index[len(index)] = i
				depth[fs.name] = len(index)
				ss.m[fs.name] = fs
				ss.l = append(ss.l, fs)
			}
		}
	}
}

var (
	structSpecMutex  sync.RWMutex
	structSpecCache  = make(map[reflect.Type]*structSpec)
	defaultFieldSpec = &fieldSpec{}
)

func structSpecForType(t reflect.Type) *structSpec {

	structSpecMutex.RLock()
	ss, found := structSpecCache[t]
	structSpecMutex.RUnlock()
	if found {
		return ss
	}

	structSpecMutex.Lock()
	defer structSpecMutex.Unlock()
	ss, found = structSpecCache[t]
	if found {
		return ss
	}

	ss = &structSpec{m: make(map[string]*fieldSpec)}
	compileStructSpec(t, make(map[string]int), nil, ss)
	structSpecCache[t] = ss
	return ss
}

var errScanStructValue = errors.New("ssgo: ScanStruct value must be non-nil pointer to a struct")

// ScanStruct scans alternating names and values from src to a struct. The
// HGETALL and CONFIG GET commands return replies in this format.
//
// ScanStruct uses exported field names to match values in the response. Use
// 'ssgo' field tag to override the name:
//
//      Field int `ssgo:"myName"`
//
// Fields with the tag ssgo:"-" are ignored.
// !Note!: if field don't have ssgo: tag, ScanStruct will try to use json: tag
//
// Integer, float, boolean, string and []byte fields are supported. Scan uses the
// standard strconv package to convert bulk string values to numeric and
// boolean types.
//
// If a src element is nil, then the corresponding field is not modified.
func ScanStruct(src []string, dest interface{}) error {
	d := reflect.ValueOf(dest)
	if d.Kind() != reflect.Ptr || d.IsNil() {
		return errScanStructValue
	}
	d = d.Elem()
	if d.Kind() != reflect.Struct {
		return errScanStructValue
	}
	ss := structSpecForType(d.Type())

	if len(src)%2 != 0 {
		return errors.New("ssgo: ScanStruct expects even number of values in values")
	}

	for i := 0; i < len(src); i += 2 {
		s := src[i+1]
		if s == "" {
			continue
		}
		fs := ss.fieldSpec(src[i])
		if fs == nil {
			continue
		}
		if err := convertAssignString(d.FieldByIndex(fs.index), s); err != nil {
			return err
		}
	}
	return nil
}

// Args is a helper for constructing command arguments from structured values.
type Args []interface{}

// Add returns the result of appending value to args.
func (args Args) Add(value ...interface{}) Args {
	return append(args, value...)
}

func keyContains(s string, sl []string) bool {
	for _, v := range sl {
		if s == v {
			return true
		}
	}
	return false
}

// AddFlat returns the result of appending the flattened value of v to args.
//
// Maps are flattened by appending the alternating keys and map values to args.
//
// Slices are flattened by appending the slice elements to args.
//
// Structs are flattened by appending the alternating names and values of
// exported fields to args. If v is a nil struct pointer, then nothing is
// appended. The 'ssgo' field tag overrides struct field names. See ScanStruct
// for more information on the use of the 'ssgo' field tag.
//
// Other types are appended to args as is.
func (args Args) AddFlat(v interface{}, keys ...string) Args {
	rv := reflect.ValueOf(v)
	switch rv.Kind() {
	case reflect.Struct:
		args = flattenStruct(args, rv, keys...)
	case reflect.Slice:
		for i := 0; i < rv.Len(); i++ {
			args = append(args, rv.Index(i).Interface())
		}
	case reflect.Map:
		for _, k := range rv.MapKeys() {
			args = append(args, k.Interface(), rv.MapIndex(k).Interface())
		}
	case reflect.Ptr:
		if rv.Type().Elem().Kind() == reflect.Struct {
			if !rv.IsNil() {
				args = flattenStruct(args, rv.Elem())
			}
		} else {
			args = append(args, v)
		}
	default:
		args = append(args, v)
	}
	return args
}

func flattenStruct(args Args, v reflect.Value, keys ...string) Args {
	ss := structSpecForType(v.Type())
	l := len(keys)
	for _, fs := range ss.l {
		fv := v.FieldByIndex(fs.index)
		if l > 0 && !keyContains(fs.name, keys) {
			continue
		}
		args = append(args, fs.name, fv.Interface())
	}
	return args
}
<|endoftext|>


<commit_before>package segygo

import (
	"bytes"
	"encoding/binary"
	"io/ioutil"
	//"log"
	"os"
	"unsafe"
	"reflect"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("segygo")
var format = logging.MustStringFormatter(
	`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
)

const Version = "0.1"
const SEGY_DESC_HDR_LEN = 3200
const SEGY_BIN_HDR_LEN = 400
const SEGY_TRACE_HDR_LEN = 240

type BinHeader struct {
	Jobid, Lino, Reno                                                                                int32
	Ntrpr, Nart                                                                                int16
	Hdt, Dto, Hns, Nso                                                                                uint16
	Format, Fold, Tsort, Vscode, Hsfs, Hsfe, Hslen, Hstyp, Schn, Hstas, Hstae, Htatyp, Hcorr, Bgrcv, Rcvm, Mfeet, Polyt, Vgpol int16
	Hunass                                                                                [170]int16 // unassigned
}

type TraceHeader struct {
	Tracel                                                               int32
	Tracer                                                               int32
	Fldr                                                                 int32
	Tracf                                                                int32
	Ep                                                                   int32
	CDP                                                                  int32
	CDPT                                                                 int32
	Trid                                                                 int16
	Nvs                                                                  int16
	Nhs                                                                  int16
	Duse                                                                 int16
	Offset                                                               int32
	Gelev                                                                int32
	Selev                                                                int32
	Sdepth                                                               int32
	Gdel                                                                 int32
	Sdel                                                                 int32
	SwDep                                                                int32
	GwDep                                                                int32
	Scalel                                                               int16
	Scalco                                                               int16
	Sx                                                                   int32
	Sy                                                                   int32
	Gx                                                                   int32
	Gy                                                                   int32
	CoUnit                                                               int16
	WeVel                                                                int16
	SweVel                                                               int16
	Sut, Gut, Sstat, Gstat, Tstat, Laga, Lagb, Delrt, Muts, Mute         int16
	Ns, Dt                                                               uint16
	Gain, Igc, Igi, Corr, Sfs, Sfe, Slen, Styp, Stas, Stae, Tatyp        int16
	Afilf, Afils, NoFilf, NoFils, Lcf, Hcf, Lcs, Hcs, Year, Day          int16
	Hour, Minute, Sec, Timbas, Trwf, Grnors, Grnofr, Grnlof, Gaps, Otrav int16
	D1, F1, D2, F2, Ungpow, Unscale                                      float32
	Ntr                                                                  int32
	Mark, Shortpad                                                       int16
	Unass                                                                [14]int16 // unassigned short array
}

type Trace struct {
	TraceHeader
	Data []float32
}

type SegyFile struct {
	Filename string
	Header   BinHeader
	NrTraces int64
	file     *os.File
	Position int64
}

func OpenFile(filename string) (SegyFile, error) {
	// Setup proper logging
	//backend1 := logging.NewLogBackend(os.Stdout, "", 0)
	//backend1Formatter := logging.NewBackendFormatter(backend1, format)
	//backend1Leveled := logging.AddModuleLevel(backend1)
	//backend1Leveled.SetLevel(logging.INFO, "")
	//logging.SetBackend(backend1Leveled, backend1Formatter)

	var s SegyFile
	var binHdr BinHeader
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return s, err
	}

	s.Filename = filename

	accum := []byte{}
	accum = append(accum, b...)

	accum2 := accum[3200:]
	r := bytes.NewReader(accum2)
	//fmt.Println("Number of bytes:", r.Len())
	log.Infof("Number of bytes: %d", r.Len())

	if err = binary.Read(r, binary.BigEndian, &binHdr); err != nil {
		log.Errorf("Error reading segy file (bigendian). %s", err)
		return s, err
	}

	// Open and store the os.File object in our struct
	file, err := os.Open(s.Filename)
	s.file = file

	s.Header = binHdr
	s.NrTraces = s.GetNrTraces()

	return s, err
}

func (s *SegyFile) GetNrTraces() int64 {
	fi, err := s.file.Stat()
	if err != nil {
		log.Error("unable to get Stat()")
		log.Fatal(err)
	}
	size := fi.Size()
	nSamples := s.Header.Hns
	txtAndBinarySize := int64(SEGY_DESC_HDR_LEN + SEGY_BIN_HDR_LEN)
	nTraces := ((size - txtAndBinarySize) / (int64(SEGY_TRACE_HDR_LEN) + int64(nSamples)*int64(unsafe.Sizeof(float32(1)))))

	return nTraces
}

func (s *SegyFile) GetNrSamples() int32 {
	return int32(s.Header.Hns)
}

func (s *SegyFile) PrintBinaryHeader() {
	v := reflect.ValueOf(s.Header)
	for i := 0; i < v.NumField(); i++ {
		log.Infof("name = %s, value = %d", v.Type().Field(i).Name, v.Field(i).Interface())
	}
}

func (s *SegyFile) ReadTrace() (Trace, error) {
	trace := Trace{}
	traceBuff := make([]float32, s.GetNrSamples())
	byteBuff := make([]byte, s.GetNrSamples()*4)
	trace.Data = traceBuff

	trcHdrBuff := make([]byte, SEGY_TRACE_HDR_LEN)
	bytesRead, err := s.file.Read(trcHdrBuff)
	if err != nil {
		log.Fatal(err)
		return trace, err
	}

	trcHdrReader := bytes.NewReader(trcHdrBuff)
	err = binary.Read(trcHdrReader, binary.BigEndian, &trace.TraceHeader)
	if err != nil {
		log.Fatal(err)
		return trace, err
	}

	bytesRead, err = s.file.Read(byteBuff)
	if err != nil {
		log.Fatal(err)
		return trace, err
	}

	if bytesRead == 0 {
		log.Infof("No bytes read for trace #", s.Position)
	}

	for i := range trace.Data {
		trace.Data[i] = float32(binary.BigEndian.Uint32(byteBuff[i*4 : (i+1)*4]))
	}

	//log.Println("ReadTrace read ", bytesRead, " bytes")

	// Then figure out the size of the data, and read it
	return trace, nil
}
<commit_msg>Enable better logging and possibility to change loglevel. Default is to only output WARNING and above.<commit_after>package segygo

import (
	"bytes"
	"encoding/binary"
	"io/ioutil"
	//"log"
	"os"
	"unsafe"
	"reflect"
	"github.com/op/go-logging"
)

var log = logging.MustGetLogger("segygo")
var format = logging.MustStringFormatter(
	`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
)

const Version = "0.1"
const SEGY_DESC_HDR_LEN = 3200
const SEGY_BIN_HDR_LEN = 400
const SEGY_TRACE_HDR_LEN = 240

type BinHeader struct {
	Jobid, Lino, Reno                                                                                int32
	Ntrpr, Nart                                                                                int16
	Hdt, Dto, Hns, Nso                                                                                uint16
	Format, Fold, Tsort, Vscode, Hsfs, Hsfe, Hslen, Hstyp, Schn, Hstas, Hstae, Htatyp, Hcorr, Bgrcv, Rcvm, Mfeet, Polyt, Vgpol int16
	Hunass                                                                                [170]int16 // unassigned
}

type TraceHeader struct {
	Tracel                                                               int32
	Tracer                                                               int32
	Fldr                                                                 int32
	Tracf                                                                int32
	Ep                                                                   int32
	CDP                                                                  int32
	CDPT                                                                 int32
	Trid                                                                 int16
	Nvs                                                                  int16
	Nhs                                                                  int16
	Duse                                                                 int16
	Offset                                                               int32
	Gelev                                                                int32
	Selev                                                                int32
	Sdepth                                                               int32
	Gdel                                                                 int32
	Sdel                                                                 int32
	SwDep                                                                int32
	GwDep                                                                int32
	Scalel                                                               int16
	Scalco                                                               int16
	Sx                                                                   int32
	Sy                                                                   int32
	Gx                                                                   int32
	Gy                                                                   int32
	CoUnit                                                               int16
	WeVel                                                                int16
	SweVel                                                               int16
	Sut, Gut, Sstat, Gstat, Tstat, Laga, Lagb, Delrt, Muts, Mute         int16
	Ns, Dt                                                               uint16
	Gain, Igc, Igi, Corr, Sfs, Sfe, Slen, Styp, Stas, Stae, Tatyp        int16
	Afilf, Afils, NoFilf, NoFils, Lcf, Hcf, Lcs, Hcs, Year, Day          int16
	Hour, Minute, Sec, Timbas, Trwf, Grnors, Grnofr, Grnlof, Gaps, Otrav int16
	D1, F1, D2, F2, Ungpow, Unscale                                      float32
	Ntr                                                                  int32
	Mark, Shortpad                                                       int16
	Unass                                                                [14]int16 // unassigned short array
}

type Trace struct {
	TraceHeader
	Data []float32
}

type SegyFile struct {
	Filename string
	Header   BinHeader
	NrTraces int64
	file     *os.File
	Position int64
	LogLevel logging.Level
}

func OpenFile(filename string) (SegyFile, error) {
	var s SegyFile
	var binHdr BinHeader
	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return s, err
	}

	s.Filename = filename
	s.LogLevel = logging.WARNING

	// Setup proper logging
	backend1 := logging.NewLogBackend(os.Stderr, "", 0)
	backend1Formatter := logging.NewBackendFormatter(backend1, format)
	logging.SetBackend(backend1Formatter)
	logging.SetLevel(logging.WARNING, "")

	accum := []byte{}
	accum = append(accum, b...)

	accum2 := accum[3200:]
	r := bytes.NewReader(accum2)
	log.Debugf("Number of bytes: %d", r.Len())

	if err = binary.Read(r, binary.BigEndian, &binHdr); err != nil {
		log.Errorf("Error reading segy file (bigendian). %s", err)
		return s, err
	}

	// Open and store the os.File object in our struct
	file, err := os.Open(s.Filename)
	s.file = file

	s.Header = binHdr
	s.NrTraces = s.GetNrTraces()

	return s, err
}

func (s *SegyFile) SetVerbose(verbose bool) {

	if verbose {
		s.LogLevel = logging.DEBUG
		logging.SetLevel(s.LogLevel, "")
	} else {
		s.LogLevel = logging.WARNING
		logging.SetLevel(s.LogLevel, "")
	}

}

func (s *SegyFile) GetNrTraces() int64 {
	fi, err := s.file.Stat()
	if err != nil {
		log.Warning("unable to get Stat()")
		log.Fatal(err)
	}
	size := fi.Size()
	nSamples := s.Header.Hns
	txtAndBinarySize := int64(SEGY_DESC_HDR_LEN + SEGY_BIN_HDR_LEN)
	nTraces := ((size - txtAndBinarySize) / (int64(SEGY_TRACE_HDR_LEN) + int64(nSamples)*int64(unsafe.Sizeof(float32(1)))))

	return nTraces
}

func (s *SegyFile) GetNrSamples() int32 {
	return int32(s.Header.Hns)
}

func (s *SegyFile) GetHeader() map[string]interface{} {
	m := make(map[string]interface{})
	v := reflect.ValueOf(s.Header)
	for i := 0; i < v.NumField(); i++ {
		key := v.Type().Field(i).Name
		val := v.Field(i).Interface()
		log.Debugf("name = %s, value = %d", key, val)
		m[key] = val
	}

	return m
}

func (s *SegyFile) ReadTrace() (Trace, error) {
	trace := Trace{}
	traceBuff := make([]float32, s.GetNrSamples())
	byteBuff := make([]byte, s.GetNrSamples()*4)
	trace.Data = traceBuff

	trcHdrBuff := make([]byte, SEGY_TRACE_HDR_LEN)
	bytesRead, err := s.file.Read(trcHdrBuff)
	if err != nil {
		log.Fatal(err)
		return trace, err
	}

	trcHdrReader := bytes.NewReader(trcHdrBuff)
	err = binary.Read(trcHdrReader, binary.BigEndian, &trace.TraceHeader)
	if err != nil {
		log.Fatal(err)
		return trace, err
	}

	bytesRead, err = s.file.Read(byteBuff)
	if err != nil {
		log.Fatal(err)
		return trace, err
	}

	if bytesRead == 0 {
		log.Infof("No bytes read for trace #", s.Position)
	}

	for i := range trace.Data {
		trace.Data[i] = float32(binary.BigEndian.Uint32(byteBuff[i*4 : (i+1)*4]))
	}

	//log.Println("ReadTrace read ", bytesRead, " bytes")

	// Then figure out the size of the data, and read it
	return trace, nil
}
<|endoftext|>


<commit_before>package esendex

import (
	"encoding/xml"
	"errors"
	"time"
)

// Message is a message to send.
type Message struct {
	To           string
	MessageType  string
	Lang         string
	Validity     int
	CharacterSet string
	Retries      int
	Body         string
}

// SendResponse gives the batchid for the sent batch and lists the details of
// each message sent.
type SendResponse struct {
	BatchID  string
	Messages []SendResponseMessage
}

// SendResponseMessage gives the details for a single sent message.
type SendResponseMessage struct {
	URI string
	ID  string
}

// Send dispatches a list of messages.
func (c *AccountClient) Send(messages []Message) (*SendResponse, error) {
	body := messageDispatchRequest{
		AccountReference: c.reference,
		Message:          make([]messageDispatchRequestMessage, len(messages)),
	}

	for i, message := range messages {
		body.Message[i] = messageDispatchRequestMessage{
			To:           message.To,
			MessageType:  message.MessageType,
			Lang:         message.Lang,
			Validity:     message.Validity,
			CharacterSet: message.CharacterSet,
			Retries:      message.Retries,
			Body:         message.Body,
		}
	}

	req, err := c.newRequest("POST", "/v1.0/messagedispatcher", &body)
	if err != nil {
		return nil, err
	}

	var v messageDispatchResponse
	resp, err := c.do(req, &v)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, errors.New("Expected 200")
	}

	response := &SendResponse{
		BatchID:  v.BatchID,
		Messages: make([]SendResponseMessage, len(v.MessageHeader)),
	}

	for i, message := range v.MessageHeader {
		response.Messages[i] = SendResponseMessage{
			URI: message.URI,
			ID:  message.ID,
		}
	}

	return response, nil
}

// Send dispatches a list of messages.
func (c *AccountClient) SendAt(sendAt time.Time, messages []Message) (*SendResponse, error) {
	body := messageDispatchRequest{
		AccountReference: c.reference,
		SendAt:           &sendAt,
		Message:          make([]messageDispatchRequestMessage, len(messages)),
	}

	for i, message := range messages {
		body.Message[i] = messageDispatchRequestMessage{
			To:           message.To,
			MessageType:  message.MessageType,
			Lang:         message.Lang,
			Validity:     message.Validity,
			CharacterSet: message.CharacterSet,
			Retries:      message.Retries,
			Body:         message.Body,
		}
	}

	req, err := c.newRequest("POST", "/v1.0/messagedispatcher", &body)
	if err != nil {
		return nil, err
	}

	var v messageDispatchResponse
	resp, err := c.do(req, &v)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, errors.New("Expected 200")
	}

	response := &SendResponse{
		BatchID:  v.BatchID,
		Messages: make([]SendResponseMessage, len(v.MessageHeader)),
	}

	for i, message := range v.MessageHeader {
		response.Messages[i] = SendResponseMessage{
			URI: message.URI,
			ID:  message.ID,
		}
	}

	return response, nil
}

type messageDispatchRequest struct {
	XMLName          xml.Name                        `xml:"messages"`
	AccountReference string                          `xml:"accountreference"`
	SendAt           *time.Time                      `xml:"sendat"`
	Message          []messageDispatchRequestMessage `xml:"message"`
}

type messageDispatchRequestMessage struct {
	To           string `xml:"to"`
	MessageType  string `xml:"type"`
	Lang         string `xml:"lang"`
	Validity     int    `xml:"validity"`
	CharacterSet string `xml:"characterset"`
	Retries      int    `xml:"retries"`
	Body         string `xml:"body"`
}

type messageDispatchResponse struct {
	XMLName       xml.Name `xml:"http://api.esendex.com/ns/ messageheaders"`
	BatchID       string   `xml:"batchid,attr"`
	MessageHeader []struct {
		URI string `xml:"uri,attr"`
		ID  string `xml:"id,attr"`
	} `xml:"messageheader"`
}
<commit_msg>Factor out common sending code<commit_after>package esendex

import (
	"encoding/xml"
	"errors"
	"time"
)

// Message is a message to send.
type Message struct {
	To           string
	MessageType  string
	Lang         string
	Validity     int
	CharacterSet string
	Retries      int
	Body         string
}

// SendResponse gives the batchid for the sent batch and lists the details of
// each message sent.
type SendResponse struct {
	BatchID  string
	Messages []SendResponseMessage
}

// SendResponseMessage gives the details for a single sent message.
type SendResponseMessage struct {
	URI string
	ID  string
}

// Send dispatches a list of messages.
func (c *AccountClient) Send(messages []Message) (*SendResponse, error) {
	body := messageDispatchRequest{
		AccountReference: c.reference,
		Message:          make([]messageDispatchRequestMessage, len(messages)),
	}

	return c.doSend(body, messages)
}

// SendAt schedules a list of messages for dispatch.
func (c *AccountClient) SendAt(sendAt time.Time, messages []Message) (*SendResponse, error) {
	body := messageDispatchRequest{
		AccountReference: c.reference,
		SendAt:           &sendAt,
		Message:          make([]messageDispatchRequestMessage, len(messages)),
	}

	return c.doSend(body, messages)
}

func (c *AccountClient) doSend(body messageDispatchRequest, messages []Message) (*SendResponse, error) {
	for i, message := range messages {
		body.Message[i] = messageDispatchRequestMessage{
			To:           message.To,
			MessageType:  message.MessageType,
			Lang:         message.Lang,
			Validity:     message.Validity,
			CharacterSet: message.CharacterSet,
			Retries:      message.Retries,
			Body:         message.Body,
		}
	}

	req, err := c.newRequest("POST", "/v1.0/messagedispatcher", &body)
	if err != nil {
		return nil, err
	}

	var v messageDispatchResponse
	resp, err := c.do(req, &v)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, errors.New("Expected 200")
	}

	response := &SendResponse{
		BatchID:  v.BatchID,
		Messages: make([]SendResponseMessage, len(v.MessageHeader)),
	}

	for i, message := range v.MessageHeader {
		response.Messages[i] = SendResponseMessage{
			URI: message.URI,
			ID:  message.ID,
		}
	}

	return response, nil
}

type messageDispatchRequest struct {
	XMLName          xml.Name                        `xml:"messages"`
	AccountReference string                          `xml:"accountreference"`
	SendAt           *time.Time                      `xml:"sendat"`
	Message          []messageDispatchRequestMessage `xml:"message"`
}

type messageDispatchRequestMessage struct {
	To           string `xml:"to"`
	MessageType  string `xml:"type"`
	Lang         string `xml:"lang"`
	Validity     int    `xml:"validity"`
	CharacterSet string `xml:"characterset"`
	Retries      int    `xml:"retries"`
	Body         string `xml:"body"`
}

type messageDispatchResponse struct {
	XMLName       xml.Name `xml:"http://api.esendex.com/ns/ messageheaders"`
	BatchID       string   `xml:"batchid,attr"`
	MessageHeader []struct {
		URI string `xml:"uri,attr"`
		ID  string `xml:"id,attr"`
	} `xml:"messageheader"`
}
<|endoftext|>