<commit_before>package main

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"encoding/json"
	"hash"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
)

type GitHubAdapter struct {
	msgbuffer *MessageBuffer
	config    *GitHubConfig
	channels  map[string]*GitHubRepositoryConfig
}

// Every GithubEvent has a method Strings that returns a slice of string with the text for the irc PRIVMSG.
type GithubEvent interface {
	Strings() []string
	GetRepository() string
}

type GithubCreate struct {
	Ref          string
	RefType      string `json:"ref_type"`
	MasterBranch string
	Description  string
	PusherType   string
	Repository   GithubRepository
	Sender       GithubUser
}

type GithubDelete struct {
	Ref        string
	RefType    string `json:"ref_type"`
	PusherType string
	Repository GithubRepository
	Sender     GithubUser
}

type GithubPush struct {
	Ref        string
	After      string
	Before     string
	Created    bool
	Deleted    bool
	Forced     bool
	Compare    string
	Commits    []GithubCommit
	HeadCommit GithubCommit `json:"head_commit"`
	Repository GithubRepository
	Pusher     GithubUser
}

type GithubCommit struct {
	Id        string
	Distinct  bool
	Message   string
	Timestamp string
	Url       string
	Author    GithubUser
	Committer GithubUser
	Added     []string
	Removed   []string
	Modified  []string
}

type GithubIssuesEvent struct {
	Action     string
	Issue      GithubIssue
	Repository GithubRepository
	Sender     GithubUser
        Label       GithubIssueLabel
}

type GithubIssue struct {
	Url         string
	LabelsUrl   string `json:"labels_url"`
	CommentsUrl string `json:"comments_url"`
	EventsUrl   string `json:"events_url"`
	HtmlUrl     string `json:"html_url"`
	Id          int
	Number      int
	Title       string
	User        GithubUser
	Labels      []GithubIssueLabel
	Assignee    GithubUser
}

type GithubIssueLabel struct {
	Url   string
	Name  string
	Color string
}

type GithubRepository struct {
	Id               uint64
	Name             string
	FullName         string `json:"full_name"`
	Owner            GithubUser
	Private          bool
	HtmlUrl          string
	Description      string
	Fork             bool
	Url              string
	ForksUrl         string
	KeysUrl          string
	CollaboratorsUrl string
	TeamsUrl         string
	HooksUrl         string
	IssueEventsUrl   string
	EventsUrl        string
	AssigneesUrl     string
	BranchesUrl      string
	TagsUrl          string
	BlobsUrl         string
	GitTagsUrl       string
	GitRefsUrl       string
	TreesUrl         string
	StatusesUrl      string
	LanguagesUrl     string
	StargazersUrl    string
	ContributorsUrl  string
	SubscribersUrl   string
	SubscriptionUrl  string
	CommitsUrl       string
	GitCommitsUrl    string
	CommentsUrl      string
	CompareUrl       string
	MergesUrl        string
	ArchiveUrl       string
	DownloadsUrl     string
	IssuesUrl        string
	PullsUrl         string
	MilestonesUrl    string
	NotificationsUrl string
	LabelsUrl        string
	ReleasesUrl      string
	CreatedAt        string
	UpdatedAt        string
	PushedAt         string
	GitUrl           string
	SshUrl           string
	CloneUrl         string
	SvnUrl           string
	Homepage         string
	Size             uint64
	StargazersCount  uint64
	WatchersCount    uint64
	Language         string
	HasIssues        bool
	HasDownloads     bool
	HasWiki          bool
	ForksCount       uint64
	MirrorUrl        string
	OpenIssuesCount  uint64
	Forks            uint64
	OpenIssues       uint64
	Watchers         uint64
	DefaultBranch    string
}

type GithubUser struct {
	Login             string
	Id                uint64
	AvatarURL         string
	GravatarId        string
	Url               string
	HtmlUrl           string
	FollowersUrl      string
	FollowingUrl      string
	GistsUrl          string
	StarredUrl        string
	SubscriptionsUrl  string
	OrganizationsUrl  string
	ReposUrl          string
	EventsUrl         string
	ReceivedEventsUrl string
	Type              string
	SiteAdmin         bool
	Name              string
	Email             string
}

// CheckMAC returns true if messageMAC is a valid HMAC tag for message.
func CheckMAC(message []byte, messageMAC string, key string) bool {
	var err error
	var mac hash.Hash
	var macdata []byte
	var macparts = strings.Split(messageMAC, "=")
	macdata, err = hex.DecodeString(macparts[1])
	if err != nil {
		log.Print("Error decoding hex digest: ", err)
		return false
	}
	switch macparts[0] {
	case "md5":
		mac = hmac.New(md5.New, []byte(key))
	case "sha1":
		mac = hmac.New(sha1.New, []byte(key))
	case "sha256":
		mac = hmac.New(sha256.New, []byte(key))
	case "sha512":
		mac = hmac.New(sha512.New, []byte(key))
	default:
		log.Print("Unsupported hash: ", macparts[0])
		return false
	}
	mac.Write(message)
	expectedMAC := mac.Sum(nil)
	return hmac.Equal(macdata, expectedMAC)
}

func (g *GithubUser) String() string {
	if len(g.Name) > 0 && len(g.Email) > 0 {
		return g.Name + " <" + g.Email + ">"
	} else if len(g.Name) > 0 {
		return g.Name
	} else if len(g.Email) > 0 {
		return g.Email
	}
	return g.Login
}

func (g *GithubRepository) String() string {
	if len(g.FullName) > 0 {
		return g.FullName
	}
	return g.Name
}

func (g *GithubCommit) String() string {
	var lines []string = strings.Split(g.Message, "\n") // Commit message
	var text string
	var short bool = len(g.Added)+len(g.Removed)+len(g.Modified) < 6 // true, if more than 6 files changed

	text = g.Author.String() + " \x02" + g.Id[0:7] + "\x0f" // First 7 characters

	if !short {
		if len(g.Added) > 0 {
			text += " \x0303" + strconv.Itoa(len(g.Added)) + " files added." + "\x0f"
		}
		if len(g.Removed) > 0 {
			text += " \x0304" + strconv.Itoa(len(g.Removed)) + " files removed." + "\x0f"
		}

		if len(g.Modified) > 0 {
			text += " \x0310" + strconv.Itoa(len(g.Modified)) + " files modified." + "\x0f"
		}

	} else {
		if len(g.Added) > 0 {
			text += " \x0303" + strings.Join(g.Added, " ") + "\x0f"
		}

		if len(g.Removed) > 0 {
			text += " \x0304" + strings.Join(g.Removed, " ") + "\x0f"
		}

		if len(g.Modified) > 0 {
			text += " \x0310" + strings.Join(g.Modified, " ") + "\x0f"
		}
	}
	if len(lines) > 0 {
		text += " " + lines[0]
	}
	return text
}

func (g *GithubIssuesEvent) Strings() []string {
	switch g.Action {
	case "assigned":
		return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " " + g.Issue.Title + " to " + g.Issue.Assignee.String() + " " + g.Repository.String() + "\x0f"}
	case "unassigned":
		return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " " + g.Issue.Title + " from " + g.Issue.Assignee.String() + " " + g.Repository.String() + "\x0f"}
	case "labeled":
		return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " " + g.Issue.Title + " with " + g.Label.Name + " " + g.Repository.String() + "\x0f"}
	case "unlabeled":
		return []string{"\x0303" + g.Sender.String() + "\x0f has removed " + g.Label.Name + " from Issue " + strconv.Itoa(g.Issue.Number) + " " + g.Issue.Title + " " + g.Repository.String() + "\x0f"}
	case "opened":
                return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " " + g.Issue.Title + " " + g.Repository.String() + "\x0f"}
	case "closed":
                return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " " + g.Issue.Title + " " + g.Repository.String() + "\x0f"}
	case "reopened":
                return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " " + g.Issue.Title + " " + g.Repository.String() + "\x0f"}
	default:
		log.Print("Unsupported Issue Event: " + g.Action)
	}
        return []string{"Unsupported Issue Event: " + g.Action}
}

func (g *GithubPush) Strings() []string {
	var refs []string = strings.Split(g.Ref, "/")
	var prefix string = "\x0303" + g.Repository.String() + "\x0f \x0305" + refs[len(refs)-1] + "\x0f"
	var pushes []string = make([]string, 0)

	for _, commit := range g.Commits {
		pushes = append(pushes, prefix+" "+commit.String())
		pushes = append(pushes, prefix+" "+commit.Url)
	}
	return pushes
}

func (g *GithubPush) GetRepository() string {
	return g.Repository.String()
}

func (g *GithubCreate) Strings() []string {
	return []string{"\x0303" + g.Sender.String() + "\x0f has pushed a new " + g.RefType + " \x0305" + g.Ref + "\x0f to \x0303" + g.Repository.String() + "\x0f"}
}

func (g *GithubCreate) GetRepository() string {
	return g.Repository.String()
}

func (g *GithubDelete) Strings() []string {
	return []string{"\x0303" + g.Sender.String() + "\x0f has deleted a " + g.RefType + " \x0305" + g.Ref + "\x0f from \x0303" + g.Repository.String() + "\x0f"}
}

func (g *GithubDelete) GetRepository() string {
	return g.Repository.String()
}

func (g *GithubIssuesEvent) GetRepository() string {
	return g.Repository.String()
}

func (g *GitHubAdapter) WriteGithubEvent(event GithubEvent, body []byte, signature string) error {
	var ok bool
	var githubconf *GitHubRepositoryConfig
	var err error

	err = json.Unmarshal(body, &event)

	if err != nil {
		log.Print("Error decoding github event: ", err)
		return err
	}

	githubconf, ok = g.channels[event.GetRepository()]
	if !ok {
		log.Print("Repository ", event.GetRepository(), " not configured.")
		return err
	}
	if !CheckMAC(body, signature, githubconf.GetSecret()) {
		log.Print("DEBUG Signature: ", signature)
		return err
	}
	for _, channel := range githubconf.GetIrcChannel() {
		for _, commit := range event.Strings() {
			g.msgbuffer.AddMessage(channel, commit)
		}
	}
	return err
}

func NewGitHubAdapter(msgbuffer *MessageBuffer, config *GitHubConfig) *GitHubAdapter {
	var channels = make(map[string]*GitHubRepositoryConfig)
	for _, repo := range config.Repo {
		channels[repo.GetName()] = repo
	}
	return &GitHubAdapter{
		msgbuffer: msgbuffer,
		config:    config,
		channels:  channels,
	}
}

func (g *GitHubAdapter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	var body []byte
	var err error
	body, err = ioutil.ReadAll(req.Body)
	if err != nil {
		log.Print("Error reading body: ", err)
		return
	}

	switch req.Header.Get("X-GitHub-Event") {
	case "create":
		var create GithubCreate
		g.WriteGithubEvent(&create, body, req.Header.Get("X-Hub-Signature"))
	case "delete":
		var del GithubDelete
		g.WriteGithubEvent(&del, body, req.Header.Get("X-Hub-Signature"))
	case "push":
		var push GithubPush
		g.WriteGithubEvent(&push, body, req.Header.Get("X-Hub-Signature"))
	case "issues":
		var issue GithubIssuesEvent
		g.WriteGithubEvent(&issue, body, req.Header.Get("X-Hub-Signature"))
	default:
		log.Print("Unknown GitHub event.", req.Header.Get("X-GitHub-Event"))
	}
}
<commit_msg>Handle empty Assignee strings. Better coloring.<commit_after>package main

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/hex"
	"encoding/json"
	"hash"
	"io/ioutil"
	"log"
	"net/http"
	"strconv"
	"strings"
)

type GitHubAdapter struct {
	msgbuffer *MessageBuffer
	config    *GitHubConfig
	channels  map[string]*GitHubRepositoryConfig
}

// Every GithubEvent has a method Strings that returns a slice of string with the text for the irc PRIVMSG.
type GithubEvent interface {
	Strings() []string
	GetRepository() string
}

type GithubCreate struct {
	Ref          string
	RefType      string `json:"ref_type"`
	MasterBranch string
	Description  string
	PusherType   string
	Repository   GithubRepository
	Sender       GithubUser
}

type GithubDelete struct {
	Ref        string
	RefType    string `json:"ref_type"`
	PusherType string
	Repository GithubRepository
	Sender     GithubUser
}

type GithubPush struct {
	Ref        string
	After      string
	Before     string
	Created    bool
	Deleted    bool
	Forced     bool
	Compare    string
	Commits    []GithubCommit
	HeadCommit GithubCommit `json:"head_commit"`
	Repository GithubRepository
	Pusher     GithubUser
}

type GithubCommit struct {
	Id        string
	Distinct  bool
	Message   string
	Timestamp string
	Url       string
	Author    GithubUser
	Committer GithubUser
	Added     []string
	Removed   []string
	Modified  []string
}

type GithubIssuesEvent struct {
	Action     string
	Issue      GithubIssue
	Repository GithubRepository
	Sender     GithubUser
        Label       GithubIssueLabel
}

type GithubIssue struct {
	Url         string
	LabelsUrl   string `json:"labels_url"`
	CommentsUrl string `json:"comments_url"`
	EventsUrl   string `json:"events_url"`
	HtmlUrl     string `json:"html_url"`
	Id          int
	Number      int
	Title       string
	User        GithubUser
	Labels      []GithubIssueLabel
	Assignee    GithubUser
}

type GithubIssueLabel struct {
	Url   string
	Name  string
	Color string
}

type GithubRepository struct {
	Id               uint64
	Name             string
	FullName         string `json:"full_name"`
	Owner            GithubUser
	Private          bool
	HtmlUrl          string
	Description      string
	Fork             bool
	Url              string
	ForksUrl         string
	KeysUrl          string
	CollaboratorsUrl string
	TeamsUrl         string
	HooksUrl         string
	IssueEventsUrl   string
	EventsUrl        string
	AssigneesUrl     string
	BranchesUrl      string
	TagsUrl          string
	BlobsUrl         string
	GitTagsUrl       string
	GitRefsUrl       string
	TreesUrl         string
	StatusesUrl      string
	LanguagesUrl     string
	StargazersUrl    string
	ContributorsUrl  string
	SubscribersUrl   string
	SubscriptionUrl  string
	CommitsUrl       string
	GitCommitsUrl    string
	CommentsUrl      string
	CompareUrl       string
	MergesUrl        string
	ArchiveUrl       string
	DownloadsUrl     string
	IssuesUrl        string
	PullsUrl         string
	MilestonesUrl    string
	NotificationsUrl string
	LabelsUrl        string
	ReleasesUrl      string
	CreatedAt        string
	UpdatedAt        string
	PushedAt         string
	GitUrl           string
	SshUrl           string
	CloneUrl         string
	SvnUrl           string
	Homepage         string
	Size             uint64
	StargazersCount  uint64
	WatchersCount    uint64
	Language         string
	HasIssues        bool
	HasDownloads     bool
	HasWiki          bool
	ForksCount       uint64
	MirrorUrl        string
	OpenIssuesCount  uint64
	Forks            uint64
	OpenIssues       uint64
	Watchers         uint64
	DefaultBranch    string
}

type GithubUser struct {
	Login             string
	Id                uint64
	AvatarURL         string
	GravatarId        string
	Url               string
	HtmlUrl           string
	FollowersUrl      string
	FollowingUrl      string
	GistsUrl          string
	StarredUrl        string
	SubscriptionsUrl  string
	OrganizationsUrl  string
	ReposUrl          string
	EventsUrl         string
	ReceivedEventsUrl string
	Type              string
	SiteAdmin         bool
	Name              string
	Email             string
}

// CheckMAC returns true if messageMAC is a valid HMAC tag for message.
func CheckMAC(message []byte, messageMAC string, key string) bool {
	var err error
	var mac hash.Hash
	var macdata []byte
	var macparts = strings.Split(messageMAC, "=")
	macdata, err = hex.DecodeString(macparts[1])
	if err != nil {
		log.Print("Error decoding hex digest: ", err)
		return false
	}
	switch macparts[0] {
	case "md5":
		mac = hmac.New(md5.New, []byte(key))
	case "sha1":
		mac = hmac.New(sha1.New, []byte(key))
	case "sha256":
		mac = hmac.New(sha256.New, []byte(key))
	case "sha512":
		mac = hmac.New(sha512.New, []byte(key))
	default:
		log.Print("Unsupported hash: ", macparts[0])
		return false
	}
	mac.Write(message)
	expectedMAC := mac.Sum(nil)
	return hmac.Equal(macdata, expectedMAC)
}

func (g *GithubUser) String() string {
	if len(g.Name) > 0 && len(g.Email) > 0 {
		return g.Name + " <" + g.Email + ">"
	} else if len(g.Name) > 0 {
		return g.Name
	} else if len(g.Email) > 0 {
		return g.Email
	}
	return g.Login
}

func (g *GithubRepository) String() string {
	if len(g.FullName) > 0 {
		return g.FullName
	}
	return g.Name
}

func (g *GithubCommit) String() string {
	var lines []string = strings.Split(g.Message, "\n") // Commit message
	var text string
	var short bool = len(g.Added)+len(g.Removed)+len(g.Modified) < 6 // true, if more than 6 files changed

	text = g.Author.String() + " \x02" + g.Id[0:7] + "\x0f" // First 7 characters

	if !short {
		if len(g.Added) > 0 {
			text += " \x0303" + strconv.Itoa(len(g.Added)) + " files added." + "\x0f"
		}
		if len(g.Removed) > 0 {
			text += " \x0304" + strconv.Itoa(len(g.Removed)) + " files removed." + "\x0f"
		}

		if len(g.Modified) > 0 {
			text += " \x0310" + strconv.Itoa(len(g.Modified)) + " files modified." + "\x0f"
		}

	} else {
		if len(g.Added) > 0 {
			text += " \x0303" + strings.Join(g.Added, " ") + "\x0f"
		}

		if len(g.Removed) > 0 {
			text += " \x0304" + strings.Join(g.Removed, " ") + "\x0f"
		}

		if len(g.Modified) > 0 {
			text += " \x0310" + strings.Join(g.Modified, " ") + "\x0f"
		}
	}
	if len(lines) > 0 {
		text += " " + lines[0]
	}
	return text
}

func (g *GithubIssuesEvent) Strings() []string {
	switch g.Action {
	case "assigned":
		return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " (" + g.Issue.Title + ") to " + g.Issue.Assignee.String() + ". \x0303" + g.Repository.String() + "\x0f"}
	case "unassigned":
                if g.Issue.Assignee.String() != "" {
                        return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " (" + g.Issue.Title + ") from " + g.Issue.Assignee.String() + ". \x0303" + g.Repository.String() + "\x0f"}
                } else {
                        return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " (" + g.Issue.Title + ") from themselves. \x0303" + g.Repository.String() + "\x0f"}
                }
	case "labeled":
		return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " (" + g.Issue.Title + ") with label " + g.Label.Name + ". \x0303" + g.Repository.String() + "\x0f"}
	case "unlabeled":
		return []string{"\x0303" + g.Sender.String() + "\x0f has removed label " + g.Label.Name + " from Issue " + strconv.Itoa(g.Issue.Number) + " (" + g.Issue.Title + "). \x0303" + g.Repository.String() + "\x0f"}
	case "opened":
                return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " (" + g.Issue.Title + "). \x0303" + g.Repository.String() + "\x0f"}
	case "closed":
                return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " (" + g.Issue.Title + "). \x0303" + g.Repository.String() + "\x0f"}
	case "reopened":
                return []string{"\x0303" + g.Sender.String() + "\x0f has " + g.Action + " Issue " + strconv.Itoa(g.Issue.Number) + " (" + g.Issue.Title + "). \x0303" + g.Repository.String() + "\x0f"}
	default:
		log.Print("Unsupported Issue Event: " + g.Action)
	}
        return []string{"Unsupported Issue Event: " + g.Action}
}

func (g *GithubPush) Strings() []string {
	var refs []string = strings.Split(g.Ref, "/")
	var prefix string = "\x0303" + g.Repository.String() + "\x0f \x0305" + refs[len(refs)-1] + "\x0f"
	var pushes []string = make([]string, 0)

	for _, commit := range g.Commits {
		pushes = append(pushes, prefix+" "+commit.String())
		pushes = append(pushes, prefix+" "+commit.Url)
	}
	return pushes
}

func (g *GithubPush) GetRepository() string {
	return g.Repository.String()
}

func (g *GithubCreate) Strings() []string {
	return []string{"\x0303" + g.Sender.String() + "\x0f has pushed a new " + g.RefType + " \x0305" + g.Ref + "\x0f to \x0303" + g.Repository.String() + "\x0f"}
}

func (g *GithubCreate) GetRepository() string {
	return g.Repository.String()
}

func (g *GithubDelete) Strings() []string {
	return []string{"\x0303" + g.Sender.String() + "\x0f has deleted a " + g.RefType + " \x0305" + g.Ref + "\x0f from \x0303" + g.Repository.String() + "\x0f"}
}

func (g *GithubDelete) GetRepository() string {
	return g.Repository.String()
}

func (g *GithubIssuesEvent) GetRepository() string {
	return g.Repository.String()
}

func (g *GitHubAdapter) WriteGithubEvent(event GithubEvent, body []byte, signature string) error {
	var ok bool
	var githubconf *GitHubRepositoryConfig
	var err error

	err = json.Unmarshal(body, &event)

	if err != nil {
		log.Print("Error decoding github event: ", err)
		return err
	}

	githubconf, ok = g.channels[event.GetRepository()]
	if !ok {
		log.Print("Repository ", event.GetRepository(), " not configured.")
		return err
	}
	if !CheckMAC(body, signature, githubconf.GetSecret()) {
		log.Print("DEBUG Signature: ", signature)
		return err
	}
	for _, channel := range githubconf.GetIrcChannel() {
		for _, commit := range event.Strings() {
			g.msgbuffer.AddMessage(channel, commit)
		}
	}
	return err
}

func NewGitHubAdapter(msgbuffer *MessageBuffer, config *GitHubConfig) *GitHubAdapter {
	var channels = make(map[string]*GitHubRepositoryConfig)
	for _, repo := range config.Repo {
		channels[repo.GetName()] = repo
	}
	return &GitHubAdapter{
		msgbuffer: msgbuffer,
		config:    config,
		channels:  channels,
	}
}

func (g *GitHubAdapter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	var body []byte
	var err error
	body, err = ioutil.ReadAll(req.Body)
	if err != nil {
		log.Print("Error reading body: ", err)
		return
	}

	switch req.Header.Get("X-GitHub-Event") {
	case "create":
		var create GithubCreate
		g.WriteGithubEvent(&create, body, req.Header.Get("X-Hub-Signature"))
	case "delete":
		var del GithubDelete
		g.WriteGithubEvent(&del, body, req.Header.Get("X-Hub-Signature"))
	case "push":
		var push GithubPush
		g.WriteGithubEvent(&push, body, req.Header.Get("X-Hub-Signature"))
	case "issues":
		var issue GithubIssuesEvent
		g.WriteGithubEvent(&issue, body, req.Header.Get("X-Hub-Signature"))
	default:
		log.Print("Unknown GitHub event.", req.Header.Get("X-GitHub-Event"))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/google/go-github/github"
	"github.com/pkg/errors"
	"golang.org/x/oauth2"
)

// ErrReleaseNotFound contains the error for when a release is not found
var (
	ErrReleaseNotFound = errors.New("release is not found")
)

// GitHub contains the functions necessary for interacting with GitHub release
// objects
type GitHub interface {
	CreateRelease(ctx context.Context, req *github.RepositoryRelease) (*github.RepositoryRelease, error)
	GetRelease(ctx context.Context, tag string) (*github.RepositoryRelease, error)
	EditRelease(ctx context.Context, releaseID int64, req *github.RepositoryRelease) (*github.RepositoryRelease, error)
	DeleteRelease(ctx context.Context, releaseID int64) error
	DeleteTag(ctx context.Context, tag string) error

	UploadAsset(ctx context.Context, releaseID int64, filename string) (*github.ReleaseAsset, error)
	DeleteAsset(ctx context.Context, assetID int64) error
	ListAssets(ctx context.Context, releaseID int64) ([]*github.ReleaseAsset, error)

	SetUploadURL(urlStr string) error
}

// GitHubClient is the client for interacting with the GitHub API
type GitHubClient struct {
	Owner, Repo string
	*github.Client
}

// NewGitHubClient creates and initializes a new GitHubClient
func NewGitHubClient(owner, repo, token string, urlStr string) (GitHub, error) {
	if len(owner) == 0 {
		return nil, errors.New("missing GitHub repository owner")
	}

	if len(repo) == 0 {
		return nil, errors.New("missing GitHub repository name")
	}

	if len(token) == 0 {
		return nil, errors.New("missing GitHub API token")
	}

	if len(urlStr) == 0 {
		return nil, errors.New("missgig GitHub API URL")
	}

	baseURL, err := url.ParseRequestURI(urlStr)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse Github API URL")
	}

	ts := oauth2.StaticTokenSource(&oauth2.Token{
		AccessToken: token,
	})
	tc := oauth2.NewClient(oauth2.NoContext, ts)

	client := github.NewClient(tc)
	client.BaseURL = baseURL

	return &GitHubClient{
		Owner:  owner,
		Repo:   repo,
		Client: client,
	}, nil
}

// SetUploadURL constructs the upload URL for a release
func (c *GitHubClient) SetUploadURL(urlStr string) error {
	i := strings.Index(urlStr, "repos/")
	parsedURL, err := url.ParseRequestURI(urlStr[:i])
	if err != nil {
		return errors.Wrap(err, "faield to parse upload URL")
	}

	c.UploadURL = parsedURL
	return nil
}

// CreateRelease creates a new release object in the GitHub API
func (c *GitHubClient) CreateRelease(ctx context.Context, req *github.RepositoryRelease) (*github.RepositoryRelease, error) {

	release, res, err := c.Repositories.CreateRelease(context.TODO(), c.Owner, c.Repo, req)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create a release")
	}

	if res.StatusCode != http.StatusCreated {
		return nil, errors.Errorf("create release: invalid status: %s", res.Status)
	}

	return release, nil
}

// GetRelease queries the GitHub API for a specified release object
func (c *GitHubClient) GetRelease(ctx context.Context, tag string) (*github.RepositoryRelease, error) {
	// Check Release whether already exists or not
	release, res, err := c.Repositories.GetReleaseByTag(context.TODO(), c.Owner, c.Repo, tag)
	if err != nil {
		if res == nil {
			return nil, errors.Wrapf(err, "failed to get release tag: %s", tag)
		}

		// TODO(tcnksm): Handle invalid token
		if res.StatusCode != http.StatusNotFound {
			return nil, errors.Wrapf(err,
				"get release tag: invalid status: %s", res.Status)
		}

		return nil, ErrReleaseNotFound
	}

	return release, nil
}

func (c *GitHubClient) EditRelease(ctx context.Context, releaseID int64, req *github.RepositoryRelease) (*github.RepositoryRelease, error) {
	release, res, err := c.Repositories.EditRelease(context.TODO(), c.Owner, c.Repo, releaseID, req)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to edit release: %d", releaseID)
	}

	if res.StatusCode != http.StatusOK {
		return nil, errors.Errorf("edit release: invalid status: %s", res.Status)
	}

	return release, nil
}

// DeleteRelease deletes a release object within the GitHub API
func (c *GitHubClient) DeleteRelease(ctx context.Context, releaseID int64) error {
	res, err := c.Repositories.DeleteRelease(context.TODO(), c.Owner, c.Repo, releaseID)
	if err != nil {
		return errors.Wrap(err, "failed to delete release")
	}

	if res.StatusCode != http.StatusNoContent {
		return errors.Errorf("delete release: invalid status: %s", res.Status)
	}

	return nil
}

// DeleteTag deletes a tag from the GitHub API
func (c *GitHubClient) DeleteTag(ctx context.Context, tag string) error {
	ref := fmt.Sprintf("tags/%s", tag)
	res, err := c.Git.DeleteRef(context.TODO(), c.Owner, c.Repo, ref)
	if err != nil {
		return errors.Wrapf(err, "failed to delete tag: %s", ref)
	}

	if res.StatusCode != http.StatusNoContent {
		return errors.Errorf("delete tag: invalid status: %s", res.Status)
	}

	return nil
}

// UploadAsset uploads specified assets to a given release object
func (c *GitHubClient) UploadAsset(ctx context.Context, releaseID int64, filename string) (*github.ReleaseAsset, error) {

	filename, err := filepath.Abs(filename)
	if err != nil {
		return nil, errors.Wrap(err, "failed to get abs path")
	}

	f, err := os.Open(filename)
	if err != nil {
		return nil, errors.Wrap(err, "failed to open file")
	}

	opts := &github.UploadOptions{
		// Use base name by default
		Name: filepath.Base(filename),
	}

	asset, res, err := c.Repositories.UploadReleaseAsset(context.TODO(), c.Owner, c.Repo, releaseID, opts, f)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to upload release asset: %s", filename)
	}

	switch res.StatusCode {
	case http.StatusCreated:
		return asset, nil
	case 422:
		return nil, errors.Errorf(
			"upload release asset: invalid status code: %s",
			"422 (this is probably because the asset already uploaded)")
	default:
		return nil, errors.Errorf(
			"upload release asset: invalid status code: %s", res.Status)
	}
}

// DeleteAsset deletes assets from a given release object
func (c *GitHubClient) DeleteAsset(ctx context.Context, assetID int64) error {
	res, err := c.Repositories.DeleteReleaseAsset(context.TODO(), c.Owner, c.Repo, assetID)
	if err != nil {
		return errors.Wrap(err, "failed to delete release asset")
	}

	if res.StatusCode != http.StatusNoContent {
		return errors.Errorf("delete release assets: invalid status code: %s", res.Status)
	}

	return nil
}

// ListAssets lists assets associated with a given release
func (c *GitHubClient) ListAssets(ctx context.Context, releaseID int64) ([]*github.ReleaseAsset, error) {
	result := []*github.ReleaseAsset{}
	page := 1

	for {
		assets, res, err := c.Repositories.ListReleaseAssets(context.TODO(), c.Owner, c.Repo, releaseID, &github.ListOptions{Page: page})
		if err != nil {
			return nil, errors.Wrap(err, "failed to list assets")
		}

		if res.StatusCode != http.StatusOK {
			return nil, errors.Errorf("list release assets: invalid status code: %s", res.Status)
		}

		result = append(result, assets...)

		if res.NextPage <= page {
			break
		}

		page = res.NextPage
	}

	return result, nil
}
<commit_msg>retry up to 3 times in EditRelease<commit_after>package main

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/Songmu/retry"
	"github.com/google/go-github/github"
	"github.com/pkg/errors"
	"golang.org/x/oauth2"
)

// ErrReleaseNotFound contains the error for when a release is not found
var (
	ErrReleaseNotFound = errors.New("release is not found")
)

// GitHub contains the functions necessary for interacting with GitHub release
// objects
type GitHub interface {
	CreateRelease(ctx context.Context, req *github.RepositoryRelease) (*github.RepositoryRelease, error)
	GetRelease(ctx context.Context, tag string) (*github.RepositoryRelease, error)
	EditRelease(ctx context.Context, releaseID int64, req *github.RepositoryRelease) (*github.RepositoryRelease, error)
	DeleteRelease(ctx context.Context, releaseID int64) error
	DeleteTag(ctx context.Context, tag string) error

	UploadAsset(ctx context.Context, releaseID int64, filename string) (*github.ReleaseAsset, error)
	DeleteAsset(ctx context.Context, assetID int64) error
	ListAssets(ctx context.Context, releaseID int64) ([]*github.ReleaseAsset, error)

	SetUploadURL(urlStr string) error
}

// GitHubClient is the client for interacting with the GitHub API
type GitHubClient struct {
	Owner, Repo string
	*github.Client
}

// NewGitHubClient creates and initializes a new GitHubClient
func NewGitHubClient(owner, repo, token string, urlStr string) (GitHub, error) {
	if len(owner) == 0 {
		return nil, errors.New("missing GitHub repository owner")
	}

	if len(repo) == 0 {
		return nil, errors.New("missing GitHub repository name")
	}

	if len(token) == 0 {
		return nil, errors.New("missing GitHub API token")
	}

	if len(urlStr) == 0 {
		return nil, errors.New("missgig GitHub API URL")
	}

	baseURL, err := url.ParseRequestURI(urlStr)
	if err != nil {
		return nil, errors.Wrap(err, "failed to parse Github API URL")
	}

	ts := oauth2.StaticTokenSource(&oauth2.Token{
		AccessToken: token,
	})
	tc := oauth2.NewClient(oauth2.NoContext, ts)

	client := github.NewClient(tc)
	client.BaseURL = baseURL

	return &GitHubClient{
		Owner:  owner,
		Repo:   repo,
		Client: client,
	}, nil
}

// SetUploadURL constructs the upload URL for a release
func (c *GitHubClient) SetUploadURL(urlStr string) error {
	i := strings.Index(urlStr, "repos/")
	parsedURL, err := url.ParseRequestURI(urlStr[:i])
	if err != nil {
		return errors.Wrap(err, "faield to parse upload URL")
	}

	c.UploadURL = parsedURL
	return nil
}

// CreateRelease creates a new release object in the GitHub API
func (c *GitHubClient) CreateRelease(ctx context.Context, req *github.RepositoryRelease) (*github.RepositoryRelease, error) {

	release, res, err := c.Repositories.CreateRelease(context.TODO(), c.Owner, c.Repo, req)
	if err != nil {
		return nil, errors.Wrap(err, "failed to create a release")
	}

	if res.StatusCode != http.StatusCreated {
		return nil, errors.Errorf("create release: invalid status: %s", res.Status)
	}

	return release, nil
}

// GetRelease queries the GitHub API for a specified release object
func (c *GitHubClient) GetRelease(ctx context.Context, tag string) (*github.RepositoryRelease, error) {
	// Check Release whether already exists or not
	release, res, err := c.Repositories.GetReleaseByTag(context.TODO(), c.Owner, c.Repo, tag)
	if err != nil {
		if res == nil {
			return nil, errors.Wrapf(err, "failed to get release tag: %s", tag)
		}

		// TODO(tcnksm): Handle invalid token
		if res.StatusCode != http.StatusNotFound {
			return nil, errors.Wrapf(err,
				"get release tag: invalid status: %s", res.Status)
		}

		return nil, ErrReleaseNotFound
	}

	return release, nil
}

func (c *GitHubClient) EditRelease(ctx context.Context, releaseID int64, req *github.RepositoryRelease) (*github.RepositoryRelease, error) {
	var release *github.RepositoryRelease

	err := retry.Retry(3, 3*time.Second, func() error {
		var (
			res *github.Response
			err error
		)
		release, res, err = c.Repositories.EditRelease(context.TODO(), c.Owner, c.Repo, releaseID, req)
		if err != nil {
			return errors.Wrapf(err, "failed to edit release: %d", releaseID)
		}

		if res.StatusCode != http.StatusOK {
			return errors.Errorf("edit release: invalid status: %s", res.Status)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return release, nil
}

// DeleteRelease deletes a release object within the GitHub API
func (c *GitHubClient) DeleteRelease(ctx context.Context, releaseID int64) error {
	res, err := c.Repositories.DeleteRelease(context.TODO(), c.Owner, c.Repo, releaseID)
	if err != nil {
		return errors.Wrap(err, "failed to delete release")
	}

	if res.StatusCode != http.StatusNoContent {
		return errors.Errorf("delete release: invalid status: %s", res.Status)
	}

	return nil
}

// DeleteTag deletes a tag from the GitHub API
func (c *GitHubClient) DeleteTag(ctx context.Context, tag string) error {
	ref := fmt.Sprintf("tags/%s", tag)
	res, err := c.Git.DeleteRef(context.TODO(), c.Owner, c.Repo, ref)
	if err != nil {
		return errors.Wrapf(err, "failed to delete tag: %s", ref)
	}

	if res.StatusCode != http.StatusNoContent {
		return errors.Errorf("delete tag: invalid status: %s", res.Status)
	}

	return nil
}

// UploadAsset uploads specified assets to a given release object
func (c *GitHubClient) UploadAsset(ctx context.Context, releaseID int64, filename string) (*github.ReleaseAsset, error) {

	filename, err := filepath.Abs(filename)
	if err != nil {
		return nil, errors.Wrap(err, "failed to get abs path")
	}

	f, err := os.Open(filename)
	if err != nil {
		return nil, errors.Wrap(err, "failed to open file")
	}

	opts := &github.UploadOptions{
		// Use base name by default
		Name: filepath.Base(filename),
	}

	asset, res, err := c.Repositories.UploadReleaseAsset(context.TODO(), c.Owner, c.Repo, releaseID, opts, f)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to upload release asset: %s", filename)
	}

	switch res.StatusCode {
	case http.StatusCreated:
		return asset, nil
	case 422:
		return nil, errors.Errorf(
			"upload release asset: invalid status code: %s",
			"422 (this is probably because the asset already uploaded)")
	default:
		return nil, errors.Errorf(
			"upload release asset: invalid status code: %s", res.Status)
	}
}

// DeleteAsset deletes assets from a given release object
func (c *GitHubClient) DeleteAsset(ctx context.Context, assetID int64) error {
	res, err := c.Repositories.DeleteReleaseAsset(context.TODO(), c.Owner, c.Repo, assetID)
	if err != nil {
		return errors.Wrap(err, "failed to delete release asset")
	}

	if res.StatusCode != http.StatusNoContent {
		return errors.Errorf("delete release assets: invalid status code: %s", res.Status)
	}

	return nil
}

// ListAssets lists assets associated with a given release
func (c *GitHubClient) ListAssets(ctx context.Context, releaseID int64) ([]*github.ReleaseAsset, error) {
	result := []*github.ReleaseAsset{}
	page := 1

	for {
		assets, res, err := c.Repositories.ListReleaseAssets(context.TODO(), c.Owner, c.Repo, releaseID, &github.ListOptions{Page: page})
		if err != nil {
			return nil, errors.Wrap(err, "failed to list assets")
		}

		if res.StatusCode != http.StatusOK {
			return nil, errors.Errorf("list release assets: invalid status code: %s", res.Status)
		}

		result = append(result, assets...)

		if res.NextPage <= page {
			break
		}

		page = res.NextPage
	}

	return result, nil
}
<|endoftext|>


<commit_before>package dao

import (
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
	"auto-ng/config"
)

type Template struct {
	Id          int
	ProjectName string `orm:"size(50)"`
	Content     string `orm:"size(200)"`
}

var db *config.DBconfig

var ormer orm.Ormer

func init() {

	db = config.GetDBconfig()

	orm.RegisterDriver("mysql", orm.DRMySQL)

	orm.RegisterDataBase(db.Alias, db.DriverName, db.DataSource, db.Conns)

	orm.RegisterModel(new(Template))

	orm.RunSyncdb(db.Alias, false, true)

	ormer = orm.NewOrm()
}

func QueryById(id int) Template {
	t := Template{Id: id}
	err := ormer.Read(&t)
	if err != nil {
		panic(err)
	}
	return t
}

func Insert(tmpl *Template) int {
	id, err := ormer.Insert(&tmpl)
	if err != nil {
		panic(err)
	}
	return id
}

func DeleteById(id int) bool {
	t := Template{Id: id}
	num, err := ormer.Delete(&t)
	if err != nil {
		panic(err)
	}
	return num == 1
}

func Update(tmpl *Template) bool {
	num, err := ormer.Update(&tmpl)
	if err != nil {
		panic(err)
	}
	return num == 1
}<commit_msg>添加备注  修改bug<commit_after>package dao

import (
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"
	"auto-ng/config"
)

type Template struct {
	Id          int
	ProjectName string `orm:"size(50)"`
	Content     string `orm:"size(200)"`
}

var db *config.DBconfig

var ormer orm.Ormer

func init() {

	db = config.GetDBconfig()

	orm.RegisterDriver("mysql", orm.DRMySQL)

	orm.RegisterDataBase(db.Alias, db.DriverName, db.DataSource, db.Conns)

	orm.RegisterModel(new(Template))

	orm.RunSyncdb(db.Alias, false, true)

	ormer = orm.NewOrm()
}

/**
	根据id查询
 */
func QueryById(id int) Template {
	t := Template{Id: id}
	err := ormer.Read(&t)
	if err != nil {
		panic(err)
	}
	return t
}

/**
	插入一条模板
 */
func Insert(tmpl Template) int {
	id, err := ormer.Insert(&tmpl)
	if err != nil {
		panic(err)
	}
	return id
}

/**
	根据id删除
 */
func DeleteById(id int) bool {
	t := Template{Id: id}
	num, err := ormer.Delete(&t)
	if err != nil {
		panic(err)
	}
	return num == 1
}

/**
	更新一条数据
 */
func Update(tmpl Template) bool {
	num, err := ormer.Update(&tmpl)
	if err != nil {
		panic(err)
	}
	return num == 1
}<|endoftext|>


<commit_before>package nakadi_test

import (
	"fmt"
	"log"
	"time"

	"github.com/stoewer/go-nakadi"
)

func Example_complete() {
	//  create a new client
	client := nakadi.New("http://localhost:8080", &nakadi.ClientOptions{ConnectionTimeout: 500 * time.Millisecond})

	// create an event api create a new event type
	eventAPI := nakadi.NewEventAPI(client, &nakadi.EventOptions{Retry: true})
	eventType := &nakadi.EventType{
		Name:                 "test-type",
		OwningApplication:    "test-app",
		Category:             "data",
		EnrichmentStrategies: []string{"metadata_enrichment"},
		PartitionStrategy:    "random",
		Schema: &nakadi.EventTypeSchema{
			Type:   "json_schema",
			Schema: `{"properties":{"test":{"type":"string"}}}`,
		},
	}
	err := eventAPI.Create(eventType)
	if err != nil {
		log.Fatal(err)
	}

	// create a new subscription API and a new subscription
	subAPI := nakadi.NewSubscriptionAPI(client, &nakadi.SubscriptionOptions{Retry: true})
	sub := &nakadi.Subscription{
		OwningApplication: "another-app",
		EventTypes:        []string{"test-type"},
		ReadFrom:          "begin",
	}
	sub, err = subAPI.Create(sub)
	if err != nil {
		log.Fatal(err)
	}

	// create a publish api and publish events
	pubAPI := nakadi.NewPublishAPI(client, eventType.Name, nil)
	event := nakadi.DataChangeEvent{
		Metadata: nakadi.EventMetadata{
			EID:        "9aabcd94-7ebd-11e7-898b-97df92934aa5",
			OccurredAt: time.Now(),
		},
		Data:     map[string]string{"test": "some value"},
		DataOP:   "U",
		DataType: "test",
	}
	err = pubAPI.PublishDataChangeEvent([]nakadi.DataChangeEvent{event})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("event published")

	// create a new stream and read one event
	stream := nakadi.NewStream(client, sub.ID, nil)
	cursor, _, err := stream.NextEvents()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("1 event received")

	stream.CommitCursor(cursor)
	stream.Close()

	subAPI.Delete(sub.ID)
	eventAPI.Delete(eventType.Name)

	// Output: event published
	// 1 event received
}
<commit_msg>fixing Example_complete test<commit_after>package nakadi_test

import (
	"fmt"
	"log"
	"time"

	"github.com/stoewer/go-nakadi"
)

func Example_complete() {
	//  create a new client
	client := nakadi.New("http://localhost:8080", &nakadi.ClientOptions{ConnectionTimeout: 500 * time.Millisecond})

	// create an event api create a new event type

	eventAPI := nakadi.NewEventAPI(client, &nakadi.EventOptions{Retry: true})
	eventType := &nakadi.EventType{
		Name:                 "test-type",
		OwningApplication:    "test-app",
		Category:             "data",
		EnrichmentStrategies: []string{"metadata_enrichment"},
		PartitionStrategy:    "random",
		Schema: &nakadi.EventTypeSchema{
			Type:   "json_schema",
			Schema: `{"properties":{"test":{"type":"string"}}}`,
		},
	}
	err := eventAPI.Create(eventType)
	if err != nil {
		log.Fatal(err)
	}

	// create a new subscription API and a new subscription

	subAPI := nakadi.NewSubscriptionAPI(client, &nakadi.SubscriptionOptions{Retry: true})
	auth := SubscriptionAuthorization{
		Admins:  []AuthorizationAttribute{{DataType: "service", Value: "test-service"}},
		Readers: []AuthorizationAttribute{{DataType: "service", Value: "test-service"}},
	}
	sub := &nakadi.Subscription{
		OwningApplication: "another-app",
		EventTypes:        []string{"test-type"},
		ReadFrom:          "begin",
		Authorization:     auth,
	}
	sub, err = subAPI.Create(sub)
	if err != nil {
		log.Fatal(err)
	}

	// create a publish api and publish events
	pubAPI := nakadi.NewPublishAPI(client, eventType.Name, nil)
	event := nakadi.DataChangeEvent{
		Metadata: nakadi.EventMetadata{
			EID:        "9aabcd94-7ebd-11e7-898b-97df92934aa5",
			OccurredAt: time.Now(),
		},
		Data:     map[string]string{"test": "some value"},
		DataOP:   "U",
		DataType: "test",
	}
	err = pubAPI.PublishDataChangeEvent([]nakadi.DataChangeEvent{event})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("event published")

	// create a new stream and read one event
	stream := nakadi.NewStream(client, sub.ID, nil)
	cursor, _, err := stream.NextEvents()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("1 event received")

	stream.CommitCursor(cursor)
	stream.Close()

	subAPI.Delete(sub.ID)
	eventAPI.Delete(eventType.Name)

	// Output: event published
	// 1 event received
}
<|endoftext|>


<commit_before>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
 *  Filename:    goline.go
 *  Package:     goline
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Sat Aug 13 02:28:54 PDT 2011
 *  Description:
 */

/*
Package goline is a command line interfacing (prompting) library inspired
by Ruby's HighLine.

Differences for HighLine users:

    - To be more Go-ish, where HighLine uses the term "strip", the package
      uses "trim".

    - Instead of an `Agree(question, config) bool` function, the package
      provides a function `Confirm(question, yesorno, config) bool`. This is
      because the author things the term "agree" implies the desire of a
      positive response to the question ("yes").
*/
package goline

import (
    "reflect"
    "strings"
    "unicode"
    "bufio"
    "utf8"
    "fmt"
    "os"
)

//  A simple function for printing (single-line) messages and prompts to
//  os.Stdout. If trailing whitespace is present in the given message, it
//  will be printed as given. Otherwise, a trailing newline '\n' will be
//  printed after the message.
//      goline.Say("Hello, World!") // Prints "Hello, World!\n"
//      goline.Say("Hello, World! ") // Prints "Hello, World! "
//  See also, SayTrimmed.
func Say(msg string) (int, os.Error) {
    if c, _ := utf8.DecodeLastRuneInString(msg); unicode.IsSpace(c) {
        return fmt.Print(msg)
    }
    return fmt.Println(msg)
}

//  Like Say, but trailing whitespace is removed from the message before
//  an internal call to Say is made.
//      goline.SayTrimmed("Hello, World! \n\t\t") // Prints "Hello, World!\n"
func SayTrimmed(msg string) (int, os.Error) {
    return Say(strings.TrimRightFunc(msg, unicode.IsSpace))
}

type ListMode uint

const (
    ColumnsAcross ListMode = iota
    ColumnsDown
    Inline
    Rows
)

//  Print a list of items to os.Stdout. The list can be formatted into rows
//  or into a matrix using the ListMode argument. The third argument has
//  different meaning (and type) depending on the mode.
//      MODE        OPTION  DEFAULT     MEANING
//      Rows        n/a
//      Inline      string  " or "      Join terminal element (e.g. "a, b, or c")
//      Columns*    int     80          Maximum line width
//  If the default option is desired, it should be passed as nil.
//      goline.List([]string{"cat", "dog", "go fish"}, goline.ColumnsAcross, nil)
//      /* Outputs:
//       *  cat     dog     go fish
//       */
//      goline.List([]string{"cat", "dog", "go fish"}, goline.ColumnsDown, 15)
//      /* Outputs:
//       *  cat     go fish
//       *  dog
//       */
//      goline.List([]string{"cat", "dog", "go fish"}, goline.Inline, " and ")
//      /* Outputs:
//       *  cat, dog, and go fish
//       */
//      goline.List([]string{"cat", "dog", "go fish"}, goline.Rows, nil)
//      /* Outputs:
//       *  cat
//       *  dog
//       *  go fish
//       */
//  See subdirectory examples/goline-lists.
func List(items interface{}, mode ListMode, option interface{}) {
    ival := reflect.ValueOf(items)

    // Ensure that items is a slice type.
    if k := ival.Type().Kind(); k != reflect.Slice {
        panic(os.NewError("List given non-Slice types."))
    }

    // Stringify each entry in items.
    n := len(ival.Len())
    strs := make([]string, n)
    for i := range strs {
        v := ival.Index(i).Interface()
        switch v.(type) {
        case Stringer:
            strs[i] = v.(Stringer).String()
        case string:
            strs[i] = v.(string)
        default:
            panic(os.NewError("List items contain non-string, non-Stringer item"))
        }
    }

    // Print the list.
    switch mode {
    case ColumnsAcross:
        // There is another switch statement in the ColumnsDown action.
        fallthrough
    case ColumnsDown:
        wrap := 80

        // Try to interpret the option variable as a wrap width.
        switch option.(type) {
        case nil:
        case int:
            wrap = option.(int)
        default:
            panic(os.NewError("List option of unacceptable type"))
        }

        // Determine the width of each column, and number of columns to print.
        var width int
        for i := range strs {
            if m := len(strs[i]); m > width {
                width = m
            }
        }
        ncols := (wrap + 1) / (width + 1)

        // Treat the special case of 1 column.
        if ncols <= 1 {
            // Just print rows if no more than 1 column fits.
            for i := range strs {
                SayTrimmed(strs[i])
            }
            break
        }

        nrows := (n + ncols - 1) / ncols

        // Pad each string so that it is one column wide.
        sfmt := fmt.Sprintf("%%-%ds", width) // e.g. "%20"
        for i := range strs {
            strs[i] = fmt.Sprintf(sfmt, strs[i])
        }

        // Print the list according to the mode.
        switch mode {
        case ColumnsAcross:
            for i := 0; i < n; i += ncols {
                // Determine the number of elements in the row.
                end := i + ncols
                if end > n {
                    end = n
                }

                // Print the row (trimming excess padding).
                row := strs[i:end]
                SayTrimmed(strings.Join(row, " "))
            }
        case ColumnsDown:
            for i := 0; i < nrows; i++ {
                // Select the items in the row using column-major order.
                var row []string
                for j := 0; j < ncols; j++ {
                    index := j*nrows + i
                    if index >= n {
                        break
                    }
                    row = append(row, strs[index])
                }

                // Print the row with no excess padding.
                SayTrimmed(strings.Join(row, " "))
            }
        }
    case Inline:
        n := len(strs)

        // Handle the special zero-/single-element case.
        if n == 0 {
            Say("")
            break
        } else if n == 1 {
            SayTrimmed(strs[0])
            break
        }

        // Try to interpret the option argument as a joining string.
        join := "or "
        switch option.(type) {
        case nil:
        case string:
            join = option.(string)
        default:
            panic(os.NewError("List option of unacceptable type"))
        }

        // Handle the special two-element case.
        if n == 2 {
            SayTrimmed(strings.Join([]string{strs[0], " ", join, strs[1], "\n"}, ""))
            break
        }

        // Create and print the inline list.
        strs[n-1] = join + strs[n-1]
        SayTrimmed(strings.Join(strs, ", "))
    case Rows:
        // Print each item on its own row.
        for i := range strs {
            SayTrimmed(strs[i])
        }
    default:
        panic(os.NewError("Unknown mode"))
    }
}

//  Prompt the user for text input. The result is stored in dest, which must
//  be a pointer to a native Go type (int, uint16, string, float32, ...).
//  Slice types are not currently supported. List input must be done with a
//  *string destination and post-processing.
//      package main
//      import (
//          "goline"
//          "os"
//      )
//      func main() {
//          timeout := 5e3
//          goline.Ask(&timeout, "Timeout (ms)? ", func(q *goline.Question) {
//              q.Default = timeout
//              q.In(goline.IntBoundedStrictly(goline.Above, 0))
//              q.Panic = func(e os.Error) { panic(e) }
//          })
//      }
func Ask(dest interface{}, msg string, config func(*Question)) (e os.Error) {
    var q *Question
    defer func() {
        // Recover from thrown errors and use the question's Panic method instead.
        if err := recover(); err != nil {
            switch err.(type) {
            case os.Error:
                // Call a panic method...
                if q.Panic != nil {
                    q.Panic(err.(os.Error))
                }
            default:
                panic(err)
            }
        }
    }()

    // Check the type of the dest variable.
    if k := reflect.TypeOf(dest).Kind(); k != reflect.Ptr && k != reflect.Slice {
        panicUnrecoverable(fmt.Errorf("Ask(...) requires a Ptr type, not %s", k.String()))
        return
    } else if k == reflect.Slice {
        panicUnrecoverable(fmt.Errorf("Ask(...) can not currently assign to slices."))
        return
    }

    // Determine the question type from the destination.
    t, err := TypeOf(reflect.Indirect(reflect.ValueOf(dest)).Interface())
    if err != nil {
        panic(err)
    }

    // Create a new Question and configure it.
    q = newQuestion(t)
    q.Question = msg
    if config != nil {
        config(q)
    }

    // Attempt to short circuit if a first-answer was configured.
    if err := q.tryFirstAnswer(); err == nil && q.val != nil {
        if err := q.setDest(dest); err != nil {
            panicUnrecoverable(err)
            q.val = nil
        }
        return
    }

    // Prepare the prompt and an error function to reset it.
    prompt := msg
    contFunc := func(err os.Error) {
        Say(fmt.Sprintf("Error: %s\n", err.String()))
        prompt = q.Responses[AskOnError]
    }

    // Prompt the user and interpret the next line of input.
    r := bufio.NewReader(os.Stdin)
    for {
        // Say the prompt, preserving the trailing space.
        tail := stringSuffixFunc(prompt, unicode.IsSpace)
        Say(prompt + q.defaultString(tail))

        // Read a line of input.
        var resp []byte
        for cont := true; cont; {
            s, isPrefix, err := r.ReadLine()
            if err != nil {
                panicUnrecoverable(err)
                return
            }
            resp = append(resp, s...)
            cont = isPrefix
        }

        // Parse the input and check for errors.
        if err := q.parse(string(resp)); err != nil {
            panicUnrecoverable(err)
            contFunc(err)
            continue
        }

        // Cast the result from a wide (e.g. 64bit) type to the desired type.
        // This should not fail under any normal circumstances, so failure
        // should break the loop.
        if err := q.setDest(dest); err != nil {
            panicUnrecoverable(err)
            contFunc(err)
            continue
        }
        break
    }
    return
}

//  Corresponds to HighLine's `agree` method. A simple wrapper around Ask for
//  yes/no questions. Confirm is given a string to prompt the user with, and a
//  default (or expected) value (yes=true, no=false). Returns the value of the
//  input.
//      if Confirm("Fetch data from the server? ", true, nil) {
//          var server string
//          Ask(&server, "Server (host:port)? ", nil)
//          // Fetch some data...
//      }
func Confirm(question string, yes bool, config func(*Question)) bool {
    def := "no"
    if yes {
        def = "yes"
    }

    // Ask a yes/no question.
    var okstr string
    var err os.Error
    Ask(&okstr, question, func(q *Question) {
        q.Default = def
        q.In(StringSet{"yes", "y", "no", "n"})
        if config != nil {
            config(q)
        }
        if q.Panic != nil {
            f := q.Panic
            q.Panic = func(e os.Error) {
                err = e
                f(e)
            }
        }
    })

    // Interpret the result.
    if err != nil {
        return panic(err)
    }
    if okstr[0] == 'y' {
        return true
    }
    return false
}

//  Prompt the user to choose from a list of choices. Return the index
//  of the chosen item, and the item itself in an empty interface. See
//  Menu for more information about configuring the prompt.
func Choose(config func(*Menu)) (i int, v interface{}) {
    i = -1 // The chosen index.

    // Create a Menu and run the config function.
    m := newMenu()
    config(m)
    if m.Len() == 0 {
        if m.Panic != nil {
            m.Panic(ErrorNoChoices)
            return
        }
    }

    // Print the menu header if there is one.
    if len(m.Header) > 0 {
        Say(m.Header)
    }

    // Display the list.
    raw, selections, tr := m.Selections()
    List(raw, m.ListMode, nil)
    ok := true

    // Ask for a selection.
    var resp string
    Ask(&resp, m.Question, func(q *Question) {
        q.In(StringSet(selections))
        q.Panic = func(err os.Error) {
            ok = false
            m.Panic(err)
        }
    })
    if !ok {
        return
    }

    // Translate the response into an index and choice.
    i = tr[resp]
    v = m.Choices[i]

    // Return the index and choice selected.
    return
}
<commit_msg>Fix minor errors from refactor.<commit_after>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
 *  Filename:    goline.go
 *  Package:     goline
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Sat Aug 13 02:28:54 PDT 2011
 *  Description:
 */

/*
Package goline is a command line interfacing (prompting) library inspired
by Ruby's HighLine.

Differences for HighLine users:

    - To be more Go-ish, where HighLine uses the term "strip", the package
      uses "trim".

    - Instead of an `Agree(question, config) bool` function, the package
      provides a function `Confirm(question, yesorno, config) bool`. This is
      because the author things the term "agree" implies the desire of a
      positive response to the question ("yes").
*/
package goline

import (
    "reflect"
    "strings"
    "unicode"
    "bufio"
    "utf8"
    "fmt"
    "os"
)

//  A simple function for printing (single-line) messages and prompts to
//  os.Stdout. If trailing whitespace is present in the given message, it
//  will be printed as given. Otherwise, a trailing newline '\n' will be
//  printed after the message.
//      goline.Say("Hello, World!") // Prints "Hello, World!\n"
//      goline.Say("Hello, World! ") // Prints "Hello, World! "
//  See also, SayTrimmed.
func Say(msg string) (int, os.Error) {
    if c, _ := utf8.DecodeLastRuneInString(msg); unicode.IsSpace(c) {
        return fmt.Print(msg)
    }
    return fmt.Println(msg)
}

//  Like Say, but trailing whitespace is removed from the message before
//  an internal call to Say is made.
//      goline.SayTrimmed("Hello, World! \n\t\t") // Prints "Hello, World!\n"
func SayTrimmed(msg string) (int, os.Error) {
    return Say(strings.TrimRightFunc(msg, unicode.IsSpace))
}

type ListMode uint

const (
    ColumnsAcross ListMode = iota
    ColumnsDown
    Inline
    Rows
)

//  Print a list of items to os.Stdout. The list can be formatted into rows
//  or into a matrix using the ListMode argument. The third argument has
//  different meaning (and type) depending on the mode.
//      MODE        OPTION  DEFAULT     MEANING
//      Rows        n/a
//      Inline      string  " or "      Join terminal element (e.g. "a, b, or c")
//      Columns*    int     80          Maximum line width
//  If the default option is desired, it should be passed as nil.
//      goline.List([]string{"cat", "dog", "go fish"}, goline.ColumnsAcross, nil)
//      /* Outputs:
//       *  cat     dog     go fish
//       */
//      goline.List([]string{"cat", "dog", "go fish"}, goline.ColumnsDown, 15)
//      /* Outputs:
//       *  cat     go fish
//       *  dog
//       */
//      goline.List([]string{"cat", "dog", "go fish"}, goline.Inline, " and ")
//      /* Outputs:
//       *  cat, dog, and go fish
//       */
//      goline.List([]string{"cat", "dog", "go fish"}, goline.Rows, nil)
//      /* Outputs:
//       *  cat
//       *  dog
//       *  go fish
//       */
//  See subdirectory examples/goline-lists.
func List(items interface{}, mode ListMode, option interface{}) {
    ival := reflect.ValueOf(items)

    // Ensure that items is a slice type.
    if k := ival.Type().Kind(); k != reflect.Slice {
        panic(os.NewError("List given non-Slice types."))
    }

    // Stringify each entry in items.
    n := ival.Len()
    strs := make([]string, n)
    for i := range strs {
        v := ival.Index(i).Interface()
        switch v.(type) {
        case Stringer:
            strs[i] = v.(Stringer).String()
        case string:
            strs[i] = v.(string)
        default:
            panic(os.NewError("List items contain non-string, non-Stringer item"))
        }
    }

    // Print the list.
    switch mode {
    case ColumnsAcross:
        // There is another switch statement in the ColumnsDown action.
        fallthrough
    case ColumnsDown:
        wrap := 80

        // Try to interpret the option variable as a wrap width.
        switch option.(type) {
        case nil:
        case int:
            wrap = option.(int)
        default:
            panic(os.NewError("List option of unacceptable type"))
        }

        // Determine the width of each column, and number of columns to print.
        var width int
        for i := range strs {
            if m := len(strs[i]); m > width {
                width = m
            }
        }
        ncols := (wrap + 1) / (width + 1)

        // Treat the special case of 1 column.
        if ncols <= 1 {
            // Just print rows if no more than 1 column fits.
            for i := range strs {
                SayTrimmed(strs[i])
            }
            break
        }

        nrows := (n + ncols - 1) / ncols

        // Pad each string so that it is one column wide.
        sfmt := fmt.Sprintf("%%-%ds", width) // e.g. "%20"
        for i := range strs {
            strs[i] = fmt.Sprintf(sfmt, strs[i])
        }

        // Print the list according to the mode.
        switch mode {
        case ColumnsAcross:
            for i := 0; i < n; i += ncols {
                // Determine the number of elements in the row.
                end := i + ncols
                if end > n {
                    end = n
                }

                // Print the row (trimming excess padding).
                row := strs[i:end]
                SayTrimmed(strings.Join(row, " "))
            }
        case ColumnsDown:
            for i := 0; i < nrows; i++ {
                // Select the items in the row using column-major order.
                var row []string
                for j := 0; j < ncols; j++ {
                    index := j*nrows + i
                    if index >= n {
                        break
                    }
                    row = append(row, strs[index])
                }

                // Print the row with no excess padding.
                SayTrimmed(strings.Join(row, " "))
            }
        }
    case Inline:
        n := len(strs)

        // Handle the special zero-/single-element case.
        if n == 0 {
            Say("")
            break
        } else if n == 1 {
            SayTrimmed(strs[0])
            break
        }

        // Try to interpret the option argument as a joining string.
        join := "or "
        switch option.(type) {
        case nil:
        case string:
            join = option.(string)
        default:
            panic(os.NewError("List option of unacceptable type"))
        }

        // Handle the special two-element case.
        if n == 2 {
            SayTrimmed(strings.Join([]string{strs[0], " ", join, strs[1], "\n"}, ""))
            break
        }

        // Create and print the inline list.
        strs[n-1] = join + strs[n-1]
        SayTrimmed(strings.Join(strs, ", "))
    case Rows:
        // Print each item on its own row.
        for i := range strs {
            SayTrimmed(strs[i])
        }
    default:
        panic(os.NewError("Unknown mode"))
    }
}

//  Prompt the user for text input. The result is stored in dest, which must
//  be a pointer to a native Go type (int, uint16, string, float32, ...).
//  Slice types are not currently supported. List input must be done with a
//  *string destination and post-processing.
//      package main
//      import (
//          "goline"
//          "os"
//      )
//      func main() {
//          timeout := 5e3
//          goline.Ask(&timeout, "Timeout (ms)? ", func(q *goline.Question) {
//              q.Default = timeout
//              q.In(goline.IntBoundedStrictly(goline.Above, 0))
//              q.Panic = func(e os.Error) { panic(e) }
//          })
//      }
func Ask(dest interface{}, msg string, config func(*Question)) (e os.Error) {
    var q *Question
    defer func() {
        // Recover from thrown errors and use the question's Panic method instead.
        if err := recover(); err != nil {
            switch err.(type) {
            case os.Error:
                // Call a panic method...
                if q.Panic != nil {
                    q.Panic(err.(os.Error))
                }
            default:
                panic(err)
            }
        }
    }()

    // Check the type of the dest variable.
    if k := reflect.TypeOf(dest).Kind(); k != reflect.Ptr && k != reflect.Slice {
        panicUnrecoverable(fmt.Errorf("Ask(...) requires a Ptr type, not %s", k.String()))
        return
    } else if k == reflect.Slice {
        panicUnrecoverable(fmt.Errorf("Ask(...) can not currently assign to slices."))
        return
    }

    // Determine the question type from the destination.
    t, err := TypeOf(reflect.Indirect(reflect.ValueOf(dest)).Interface())
    if err != nil {
        panic(err)
    }

    // Create a new Question and configure it.
    q = newQuestion(t)
    q.Question = msg
    if config != nil {
        config(q)
    }

    // Attempt to short circuit if a first-answer was configured.
    if err := q.tryFirstAnswer(); err == nil && q.val != nil {
        if err := q.setDest(dest); err != nil {
            panicUnrecoverable(err)
            q.val = nil
        }
        return
    }

    // Prepare the prompt and an error function to reset it.
    prompt := msg
    contFunc := func(err os.Error) {
        Say(fmt.Sprintf("Error: %s\n", err.String()))
        prompt = q.Responses[AskOnError]
    }

    // Prompt the user and interpret the next line of input.
    r := bufio.NewReader(os.Stdin)
    for {
        // Say the prompt, preserving the trailing space.
        tail := stringSuffixFunc(prompt, unicode.IsSpace)
        Say(prompt + q.defaultString(tail))

        // Read a line of input.
        var resp []byte
        for cont := true; cont; {
            s, isPrefix, err := r.ReadLine()
            if err != nil {
                panicUnrecoverable(err)
                return
            }
            resp = append(resp, s...)
            cont = isPrefix
        }

        // Parse the input and check for errors.
        if err := q.parse(string(resp)); err != nil {
            panicUnrecoverable(err)
            contFunc(err)
            continue
        }

        // Cast the result from a wide (e.g. 64bit) type to the desired type.
        // This should not fail under any normal circumstances, so failure
        // should break the loop.
        if err := q.setDest(dest); err != nil {
            panicUnrecoverable(err)
            contFunc(err)
            continue
        }
        break
    }
    return
}

//  Corresponds to HighLine's `agree` method. A simple wrapper around Ask for
//  yes/no questions. Confirm is given a string to prompt the user with, and a
//  default (or expected) value (yes=true, no=false). Returns the value of the
//  input.
//      if Confirm("Fetch data from the server? ", true, nil) {
//          var server string
//          Ask(&server, "Server (host:port)? ", nil)
//          // Fetch some data...
//      }
func Confirm(question string, yes bool, config func(*Question)) bool {
    def := "no"
    if yes {
        def = "yes"
    }

    // Ask a yes/no question.
    var okstr string
    var err os.Error
    Ask(&okstr, question, func(q *Question) {
        q.Default = def
        q.In(StringSet{"yes", "y", "no", "n"})
        if config != nil {
            config(q)
        }
        if q.Panic != nil {
            f := q.Panic
            q.Panic = func(e os.Error) {
                err = e
                f(e)
            }
        }
    })

    // Interpret the result.
    if err != nil {
        panic(err)
    }
    if okstr[0] == 'y' {
        return true
    }
    return false
}

//  Prompt the user to choose from a list of choices. Return the index
//  of the chosen item, and the item itself in an empty interface. See
//  Menu for more information about configuring the prompt.
func Choose(config func(*Menu)) (i int, v interface{}) {
    i = -1 // The chosen index.

    // Create a Menu and run the config function.
    m := newMenu()
    config(m)
    if m.Len() == 0 {
        if m.Panic != nil {
            m.Panic(ErrorNoChoices)
            return
        }
    }

    // Print the menu header if there is one.
    if len(m.Header) > 0 {
        Say(m.Header)
    }

    // Display the list.
    raw, selections, tr := m.Selections()
    List(raw, m.ListMode, nil)
    ok := true

    // Ask for a selection.
    var resp string
    Ask(&resp, m.Question, func(q *Question) {
        q.In(StringSet(selections))
        q.Panic = func(err os.Error) {
            ok = false
            m.Panic(err)
        }
    })
    if !ok {
        return
    }

    // Translate the response into an index and choice.
    i = tr[resp]
    v = m.Choices[i]

    // Return the index and choice selected.
    return
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 The libusb developers. All rights reserved.
// Project site: https://github.com/gotmc/libusb
// Use of this source code is governed by a MIT-style license that
// can be found in the LICENSE.txt file for the project.

package libusb

// #cgo pkg-config: libusb-1.0
// #include <libusb.h>
import "C"
import (
	"fmt"
	"unsafe"
)

// DeviceHandle represents the libusb device handle.
type DeviceHandle struct {
	libusbDeviceHandle *C.libusb_device_handle
}

func (dh *DeviceHandle) GetStringDescriptor(
	descIndex uint8,
	langID uint16,
) (string, error) {
	var cData *C.uchar
	length := 512
	usberr := C.libusb_get_string_descriptor(
		dh.libusbDeviceHandle,
		C.uint8_t(descIndex),
		C.uint16_t(langID),
		cData,
		C.int(length),
	)
	if usberr < 0 {
		return "", ErrorCode(usberr)
	}
	data := (*C.char)(unsafe.Pointer(cData))
	return C.GoString(data), nil
}

// GetStringDescriptorASCII is a wrapper around the
// C.libusb_string_descriptor_ascii function which retrieves an ASCII string
// descriptor using the given index of the descriptor to retrieve.
func (dh *DeviceHandle) GetStringDescriptorASCII(
	descIndex uint8,
) (string, error) {
	// TODO(mdr): Should the length be a constant? Why did I pick 256 bytes?
	length := 256
	data := make([]byte, length)
	bytesRead, err := C.libusb_get_string_descriptor_ascii(
		dh.libusbDeviceHandle,
		C.uint8_t(descIndex),
		// Unsafe pointer -> http://stackoverflow.com/a/16376039/95592
		(*C.uchar)(unsafe.Pointer(&data[0])),
		C.int(length),
	)
	if err != nil {
		return "", err
	}
	if bytesRead < 0 {
		return "", fmt.Errorf("%s", err)
	}
	return string(data[0:bytesRead]), nil
}

// Close implements libusb_close to close the device handle.
func (dh *DeviceHandle) Close() error {
	C.libusb_close(dh.libusbDeviceHandle)
	return nil
}

// GetDevice implements libusb_get_device to get the underlying device for a
// handle.
// TODO(mdr): Determine if I actually need this function.
// func (dh *DeviceHandle) GetDevice() (*Device, error) {
// }

// GetConfiguration implements the libusb_get_configuration function to
// determine the bConfigurationValue of the currently active configuration.
func (dh *DeviceHandle) GetConfiguration() (int, error) {
	var configuration *C.int
	err := C.libusb_get_configuration(dh.libusbDeviceHandle, configuration)
	if err != 0 {
		return 0, ErrorCode(err)
	}
	return int(*configuration), nil
}

// SetConfiguration implements libusb_set_configuration to set the active
// configuration for the device.
func (dh *DeviceHandle) SetConfiguration(configuration int) error {
	err := C.libusb_set_configuration(dh.libusbDeviceHandle,
		C.int(configuration))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// ClaimInterface implements libusb_claim_interface to claim an interface on a
// given device handle. You must claim the interface you wish to use before you
// can perform I/O on any of its endpoints.
func (dh *DeviceHandle) ClaimInterface(interfaceNum int) error {
	err := C.libusb_claim_interface(dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// ReleaseInterface implements libusb_release_interface to release an interface
// previously claimed with libusb_claim_interface() (i.e., ClaimInterface()).
func (dh *DeviceHandle) ReleaseInterface(interfaceNum int) error {
	err := C.libusb_release_interface(dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

func (dh *DeviceHandle) SetInterfaceAltSetting(
	interfaceNum int,
	alternateSetting int,
) error {
	err := C.libusb_set_interface_alt_setting(
		dh.libusbDeviceHandle,
		C.int(interfaceNum),
		C.int(alternateSetting),
	)
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// FIXME(mdr): libusb_clear_halt takes an endpoint as an unsigned char. Need to
// determine, what I should pass into this function as the endpoint.
// func (dh *DeviceHandle) ClearHalt(endpoint int) error {
// return nil
// }

// ResetDevice implements libusb_reset_device to perform a USB port reset to
// reinitialize a device.
func (dh *DeviceHandle) ResetDevice() error {
	err := C.libusb_reset_device(dh.libusbDeviceHandle)
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// KernelDriverActive implements libusb_kernel_driver_active to determine if a
// kernel driver is active on an interface.
func (dh *DeviceHandle) KernelDriverActive(interfaceNum int) error {
	err := C.libusb_kernel_driver_active(
		dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// DetachKernelDriver implements libusb_detach_kernel_driver to detach a kernel
// driver from an interface.
func (dh *DeviceHandle) DetachKernelDriver(interfaceNum int) error {
	err := C.libusb_detach_kernel_driver(
		dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// AttachKernelDriver implements libusb_attach_kernel_driver to re-attach an
// interface's kernel driver, which was previously detached using
// libusb_detach_kernel_driver().
func (dh *DeviceHandle) AttachKernelDriver(interfaceNum int) error {
	err := C.libusb_attach_kernel_driver(
		dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// SetAutoDetachKernelDriver implements libusb_set_auto_detach_kernel_driver to
// enable/disable libusb's automatic kernel driver detachment.
func (dh *DeviceHandle) SetAutoDetachKernelDriver(enable bool) error {
	cEnable := C.int(0)
	if enable == true {
		cEnable = C.int(1)
	}
	err := C.libusb_set_auto_detach_kernel_driver(dh.libusbDeviceHandle, cEnable)
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}
<commit_msg>Fix handling libusb_get_string_descriptor_ascii rv<commit_after>// Copyright (c) 2015 The libusb developers. All rights reserved.
// Project site: https://github.com/gotmc/libusb
// Use of this source code is governed by a MIT-style license that
// can be found in the LICENSE.txt file for the project.

package libusb

// #cgo pkg-config: libusb-1.0
// #include <libusb.h>
import "C"
import (
	"unsafe"
)

// DeviceHandle represents the libusb device handle.
type DeviceHandle struct {
	libusbDeviceHandle *C.libusb_device_handle
}

func (dh *DeviceHandle) GetStringDescriptor(
	descIndex uint8,
	langID uint16,
) (string, error) {
	var cData *C.uchar
	length := 512
	usberr := C.libusb_get_string_descriptor(
		dh.libusbDeviceHandle,
		C.uint8_t(descIndex),
		C.uint16_t(langID),
		cData,
		C.int(length),
	)
	if usberr < 0 {
		return "", ErrorCode(usberr)
	}
	data := (*C.char)(unsafe.Pointer(cData))
	return C.GoString(data), nil
}

// GetStringDescriptorASCII is a wrapper around the
// C.libusb_string_descriptor_ascii function which retrieves an ASCII string
// descriptor using the given index of the descriptor to retrieve.
func (dh *DeviceHandle) GetStringDescriptorASCII(
	descIndex uint8,
) (string, error) {
	// TODO(mdr): Should the length be a constant? Why did I pick 256 bytes?
	length := 256
	data := make([]byte, length)
	bytesRead, _ := C.libusb_get_string_descriptor_ascii(
		dh.libusbDeviceHandle,
		C.uint8_t(descIndex),
		// Unsafe pointer -> http://stackoverflow.com/a/16376039/95592
		(*C.uchar)(unsafe.Pointer(&data[0])),
		C.int(length),
	)
	if bytesRead < 0 {
		return "", ErrorCode(bytesRead)
	}
	return string(data[0:bytesRead]), nil
}

// Close implements libusb_close to close the device handle.
func (dh *DeviceHandle) Close() error {
	C.libusb_close(dh.libusbDeviceHandle)
	return nil
}

// GetDevice implements libusb_get_device to get the underlying device for a
// handle.
// TODO(mdr): Determine if I actually need this function.
// func (dh *DeviceHandle) GetDevice() (*Device, error) {
// }

// GetConfiguration implements the libusb_get_configuration function to
// determine the bConfigurationValue of the currently active configuration.
func (dh *DeviceHandle) GetConfiguration() (int, error) {
	var configuration *C.int
	err := C.libusb_get_configuration(dh.libusbDeviceHandle, configuration)
	if err != 0 {
		return 0, ErrorCode(err)
	}
	return int(*configuration), nil
}

// SetConfiguration implements libusb_set_configuration to set the active
// configuration for the device.
func (dh *DeviceHandle) SetConfiguration(configuration int) error {
	err := C.libusb_set_configuration(dh.libusbDeviceHandle,
		C.int(configuration))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// ClaimInterface implements libusb_claim_interface to claim an interface on a
// given device handle. You must claim the interface you wish to use before you
// can perform I/O on any of its endpoints.
func (dh *DeviceHandle) ClaimInterface(interfaceNum int) error {
	err := C.libusb_claim_interface(dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// ReleaseInterface implements libusb_release_interface to release an interface
// previously claimed with libusb_claim_interface() (i.e., ClaimInterface()).
func (dh *DeviceHandle) ReleaseInterface(interfaceNum int) error {
	err := C.libusb_release_interface(dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

func (dh *DeviceHandle) SetInterfaceAltSetting(
	interfaceNum int,
	alternateSetting int,
) error {
	err := C.libusb_set_interface_alt_setting(
		dh.libusbDeviceHandle,
		C.int(interfaceNum),
		C.int(alternateSetting),
	)
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// FIXME(mdr): libusb_clear_halt takes an endpoint as an unsigned char. Need to
// determine, what I should pass into this function as the endpoint.
// func (dh *DeviceHandle) ClearHalt(endpoint int) error {
// return nil
// }

// ResetDevice implements libusb_reset_device to perform a USB port reset to
// reinitialize a device.
func (dh *DeviceHandle) ResetDevice() error {
	err := C.libusb_reset_device(dh.libusbDeviceHandle)
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// KernelDriverActive implements libusb_kernel_driver_active to determine if a
// kernel driver is active on an interface.
func (dh *DeviceHandle) KernelDriverActive(interfaceNum int) error {
	err := C.libusb_kernel_driver_active(
		dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// DetachKernelDriver implements libusb_detach_kernel_driver to detach a kernel
// driver from an interface.
func (dh *DeviceHandle) DetachKernelDriver(interfaceNum int) error {
	err := C.libusb_detach_kernel_driver(
		dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// AttachKernelDriver implements libusb_attach_kernel_driver to re-attach an
// interface's kernel driver, which was previously detached using
// libusb_detach_kernel_driver().
func (dh *DeviceHandle) AttachKernelDriver(interfaceNum int) error {
	err := C.libusb_attach_kernel_driver(
		dh.libusbDeviceHandle, C.int(interfaceNum))
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}

// SetAutoDetachKernelDriver implements libusb_set_auto_detach_kernel_driver to
// enable/disable libusb's automatic kernel driver detachment.
func (dh *DeviceHandle) SetAutoDetachKernelDriver(enable bool) error {
	cEnable := C.int(0)
	if enable == true {
		cEnable = C.int(1)
	}
	err := C.libusb_set_auto_detach_kernel_driver(dh.libusbDeviceHandle, cEnable)
	if err != 0 {
		return ErrorCode(err)
	}
	return nil
}
<|endoftext|>


<commit_before>//+build go1.17

// This variant contains a trivial wrapper around "runtime/cgo".Handle.

package yara

import "runtime/cgo"

type cgoHandle cgo.Handle

func cgoNewHandle(v interface{}) cgoHandle { return cgo.NewHandle(v) }
<commit_msg>Replace cbpool...: Fix botched non-legacy version<commit_after>//+build go1.17

// This variant contains a trivial wrapper around "runtime/cgo".Handle.

package yara

import "runtime/cgo"

type cgoHandle cgo.Handle

func (h cgoHandle) Value() interface{} { return cgo.Handle(h).Value() }

func (h cgoHandle) Delete() { cgo.Handle(h).Delete() }

func cgoNewHandle(v interface{}) cgoHandle { return cgoHandle(cgo.NewHandle(v)) }
<|endoftext|>


<commit_before>package main
import "fmt"

func main() {

 fmt.Println("test config")
 fmt.Println("add new ifconfig")

 }
<commit_msg>updated ifconf.go y user2<commit_after>package main
import "fmt"

func main() {

fmt.Println("user2 added new featu2")
fmt.Println("user2 done adding new featu2")

 }
<|endoftext|>