Browse Source

add Stringify function

This provides somewhat reasonable string representations of GitHub
structs.  This is specifically designed for this library and takes a
number of shortcuts where it can, so is not suitable as a general
purpose solution.  That said, I am exporting this function because it is
useful for things like printing out slices of GitHub structs, as can be
seen in examples/example.go.

I am certainly open to suggestions for what exactly the stringified
output should look like.  Currently, I think I've found a reasonable
compromise between fmt's "%v" and "%s" output.
Will Norris 13 years ago
parent
commit
8f77def830
22 changed files with 275 additions and 3 deletions
  1. +1
    -1
      examples/example.go
  2. +12
    -0
      github/activity_events.go
  3. +8
    -0
      github/gists.go
  4. +4
    -0
      github/gists_comments.go
  5. +8
    -0
      github/git_commits.go
  6. +8
    -0
      github/git_trees.go
  7. +4
    -0
      github/issues.go
  8. +4
    -0
      github/issues_comments.go
  9. +2
    -2
      github/issues_labels.go
  10. +4
    -0
      github/orgs.go
  11. +4
    -0
      github/orgs_teams.go
  12. +4
    -0
      github/pulls.go
  13. +4
    -0
      github/pulls_comments.go
  14. +4
    -0
      github/repos.go
  15. +4
    -0
      github/repos_comments.go
  16. +16
    -0
      github/repos_hooks.go
  17. +4
    -0
      github/repos_statuses.go
  18. +4
    -0
      github/search.go
  19. +93
    -0
      github/strings.go
  20. +75
    -0
      github/strings_test.go
  21. +4
    -0
      github/timestamp.go
  22. +4
    -0
      github/users.go

+ 1
- 1
examples/example.go View File

@ -21,7 +21,7 @@ func main() {
if err != nil { if err != nil {
fmt.Printf("error: %v\n\n", err) fmt.Printf("error: %v\n\n", err)
} else { } else {
fmt.Printf("%#v\n\n", repos)
fmt.Printf("%v\n\n", github.Stringify(repos))
} }
rate, _, err := client.RateLimit() rate, _, err := client.RateLimit()


+ 12
- 0
github/activity_events.go View File

@ -25,6 +25,10 @@ type Event struct {
ID *string `json:"id,omitempty"` ID *string `json:"id,omitempty"`
} }
func (e *Event) String() string {
return Stringify(e)
}
// Payload returns the parsed event payload. For recognized event types // Payload returns the parsed event payload. For recognized event types
// (PushEvent), a value of the corresponding struct type will be returned. // (PushEvent), a value of the corresponding struct type will be returned.
func (e *Event) Payload() (payload interface{}) { func (e *Event) Payload() (payload interface{}) {
@ -49,6 +53,10 @@ type PushEvent struct {
Commits []PushEventCommit `json:"commits,omitempty"` Commits []PushEventCommit `json:"commits,omitempty"`
} }
func (p *PushEvent) String() string {
return Stringify(p)
}
// PushEventCommit represents a git commit in a GitHub PushEvent. // PushEventCommit represents a git commit in a GitHub PushEvent.
type PushEventCommit struct { type PushEventCommit struct {
SHA *string `json:"sha,omitempty"` SHA *string `json:"sha,omitempty"`
@ -58,6 +66,10 @@ type PushEventCommit struct {
Distinct *bool `json:"distinct"` Distinct *bool `json:"distinct"`
} }
func (p *PushEventCommit) String() string {
return Stringify(p)
}
// List public events. // List public events.
// //
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-public-events // GitHub API docs: http://developer.github.com/v3/activity/events/#list-public-events


+ 8
- 0
github/gists.go View File

@ -33,6 +33,10 @@ type Gist struct {
CreatedAt *time.Time `json:"created_at,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"`
} }
func (g *Gist) String() string {
return Stringify(g)
}
// GistFilename represents filename on a gist. // GistFilename represents filename on a gist.
type GistFilename string type GistFilename string
@ -44,6 +48,10 @@ type GistFile struct {
Content *string `json:"content,omitempty"` Content *string `json:"content,omitempty"`
} }
func (g *GistFile) String() string {
return Stringify(g)
}
// GistListOptions specifies the optional parameters to the // GistListOptions specifies the optional parameters to the
// GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods. // GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods.
type GistListOptions struct { type GistListOptions struct {


+ 4
- 0
github/gists_comments.go View File

@ -19,6 +19,10 @@ type GistComment struct {
CreatedAt *time.Time `json:"created_at,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"`
} }
func (g *GistComment) String() string {
return Stringify(g)
}
// ListComments lists all comments for a gist. // ListComments lists all comments for a gist.
// //
// GitHub API docs: http://developer.github.com/v3/gists/comments/#list-comments-on-a-gist // GitHub API docs: http://developer.github.com/v3/gists/comments/#list-comments-on-a-gist


+ 8
- 0
github/git_commits.go View File

@ -20,6 +20,10 @@ type Commit struct {
Parents []Commit `json:"parents,omitempty"` Parents []Commit `json:"parents,omitempty"`
} }
func (c *Commit) String() string {
return Stringify(c)
}
// CommitAuthor represents the author or committer of a commit. The commit // CommitAuthor represents the author or committer of a commit. The commit
// author may not correspond to a GitHub User. // author may not correspond to a GitHub User.
type CommitAuthor struct { type CommitAuthor struct {
@ -28,6 +32,10 @@ type CommitAuthor struct {
Email *string `json:"email,omitempty"` Email *string `json:"email,omitempty"`
} }
func (c *CommitAuthor) String() string {
return Stringify(c)
}
// GetCommit fetchs the Commit object for a given SHA. // GetCommit fetchs the Commit object for a given SHA.
// //
// GitHub API docs: http://developer.github.com/v3/git/commits/#get-a-commit // GitHub API docs: http://developer.github.com/v3/git/commits/#get-a-commit


+ 8
- 0
github/git_trees.go View File

@ -13,6 +13,10 @@ type Tree struct {
Entries []TreeEntry `json:"tree,omitempty"` Entries []TreeEntry `json:"tree,omitempty"`
} }
func (t *Tree) String() string {
return Stringify(t)
}
// TreeEntry represents the contents of a tree structure. TreeEntry can // TreeEntry represents the contents of a tree structure. TreeEntry can
// represent either a blob, a commit (in the case of a submodule), or another // represent either a blob, a commit (in the case of a submodule), or another
// tree. // tree.
@ -24,6 +28,10 @@ type TreeEntry struct {
Size *int `json:"size,omitempty"` Size *int `json:"size,omitempty"`
} }
func (t *TreeEntry) String() string {
return Stringify(t)
}
// GetTree fetches the Tree object for a given sha hash from a repository. // GetTree fetches the Tree object for a given sha hash from a repository.
// //
// GitHub API docs: http://developer.github.com/v3/git/trees/#get-a-tree // GitHub API docs: http://developer.github.com/v3/git/trees/#get-a-tree


+ 4
- 0
github/issues.go View File

@ -38,6 +38,10 @@ type Issue struct {
// TODO(willnorris): milestone // TODO(willnorris): milestone
} }
func (i *Issue) String() string {
return Stringify(i)
}
// IssueListOptions specifies the optional parameters to the IssuesService.List // IssueListOptions specifies the optional parameters to the IssuesService.List
// and IssuesService.ListByOrg methods. // and IssuesService.ListByOrg methods.
type IssueListOptions struct { type IssueListOptions struct {


+ 4
- 0
github/issues_comments.go View File

@ -20,6 +20,10 @@ type IssueComment struct {
UpdatedAt *time.Time `json:"updated_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"`
} }
func (i *IssueComment) String() string {
return Stringify(i)
}
// IssueListCommentsOptions specifies the optional parameters to the // IssueListCommentsOptions specifies the optional parameters to the
// IssuesService.ListComments method. // IssuesService.ListComments method.
type IssueListCommentsOptions struct { type IssueListCommentsOptions struct {


+ 2
- 2
github/issues_labels.go View File

@ -14,8 +14,8 @@ type Label struct {
Color *string `json:"color,omitempty"` Color *string `json:"color,omitempty"`
} }
func (label Label) String() string {
return fmt.Sprint(label.Name)
func (l Label) String() string {
return fmt.Sprint(*l.Name)
} }
// ListLabels lists all labels for a repository. // ListLabels lists all labels for a repository.


+ 4
- 0
github/orgs.go View File

@ -30,6 +30,10 @@ type Organization struct {
CreatedAt *time.Time `json:"created_at,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"`
} }
func (o *Organization) String() string {
return Stringify(o)
}
// List the organizations for a user. Passing the empty string will list // List the organizations for a user. Passing the empty string will list
// organizations for the authenticated user. // organizations for the authenticated user.
// //


+ 4
- 0
github/orgs_teams.go View File

@ -19,6 +19,10 @@ type Team struct {
ReposCount *int `json:"repos_count,omitempty"` ReposCount *int `json:"repos_count,omitempty"`
} }
func (t *Team) String() string {
return Stringify(t)
}
// ListTeams lists all of the teams for an organization. // ListTeams lists all of the teams for an organization.
// //
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#list-teams // GitHub API docs: http://developer.github.com/v3/orgs/teams/#list-teams


+ 4
- 0
github/pulls.go View File

@ -42,6 +42,10 @@ type PullRequest struct {
// TODO(willnorris): add head and base once we have a Commit struct defined somewhere // TODO(willnorris): add head and base once we have a Commit struct defined somewhere
} }
func (p *PullRequest) String() string {
return Stringify(p)
}
// PullRequestListOptions specifies the optional parameters to the // PullRequestListOptions specifies the optional parameters to the
// PullRequestsService.List method. // PullRequestsService.List method.
type PullRequestListOptions struct { type PullRequestListOptions struct {


+ 4
- 0
github/pulls_comments.go View File

@ -23,6 +23,10 @@ type PullRequestComment struct {
UpdatedAt *time.Time `json:"updated_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"`
} }
func (p *PullRequestComment) String() string {
return Stringify(p)
}
// PullRequestListCommentsOptions specifies the optional parameters to the // PullRequestListCommentsOptions specifies the optional parameters to the
// PullRequestsService.ListComments method. // PullRequestsService.ListComments method.
type PullRequestListCommentsOptions struct { type PullRequestListCommentsOptions struct {


+ 4
- 0
github/repos.go View File

@ -34,6 +34,10 @@ type Repository struct {
HasWiki *bool `json:"has_wiki"` HasWiki *bool `json:"has_wiki"`
} }
func (r *Repository) String() string {
return Stringify(r)
}
// RepositoryListOptions specifies the optional parameters to the // RepositoryListOptions specifies the optional parameters to the
// RepositoriesService.List method. // RepositoriesService.List method.
type RepositoryListOptions struct { type RepositoryListOptions struct {


+ 4
- 0
github/repos_comments.go View File

@ -26,6 +26,10 @@ type RepositoryComment struct {
Position *int `json:"position,omitempty"` Position *int `json:"position,omitempty"`
} }
func (r *RepositoryComment) String() string {
return Stringify(r)
}
// ListComments lists all the comments for the repository. // ListComments lists all the comments for the repository.
// //
// GitHub API docs: http://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository // GitHub API docs: http://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository


+ 16
- 0
github/repos_hooks.go View File

@ -33,6 +33,10 @@ type WebHookPayload struct {
Repo *Repository `json:"repository,omitempty"` Repo *Repository `json:"repository,omitempty"`
} }
func (w *WebHookPayload) String() string {
return Stringify(w)
}
// WebHookCommit represents the commit variant we receive from GitHub in a // WebHookCommit represents the commit variant we receive from GitHub in a
// WebHookPayload. // WebHookPayload.
type WebHookCommit struct { type WebHookCommit struct {
@ -47,6 +51,10 @@ type WebHookCommit struct {
Timestamp *time.Time `json:"timestamp,omitempty"` Timestamp *time.Time `json:"timestamp,omitempty"`
} }
func (w *WebHookCommit) String() string {
return Stringify(w)
}
// WebHookAuthor represents the author or committer of a commit, as specified // WebHookAuthor represents the author or committer of a commit, as specified
// in a WebHookCommit. The commit author may not correspond to a GitHub User. // in a WebHookCommit. The commit author may not correspond to a GitHub User.
type WebHookAuthor struct { type WebHookAuthor struct {
@ -55,6 +63,10 @@ type WebHookAuthor struct {
Username *string `json:"username,omitempty"` Username *string `json:"username,omitempty"`
} }
func (w *WebHookAuthor) String() string {
return Stringify(w)
}
// Hook represents a GitHub (web and service) hook for a repository. // Hook represents a GitHub (web and service) hook for a repository.
type Hook struct { type Hook struct {
CreatedAt *time.Time `json:"created_at,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"`
@ -66,6 +78,10 @@ type Hook struct {
ID *int `json:"id,omitempty"` ID *int `json:"id,omitempty"`
} }
func (h *Hook) String() string {
return Stringify(h)
}
// CreateHook creates a Hook for the specified repository. // CreateHook creates a Hook for the specified repository.
// Name and Config are required fields. // Name and Config are required fields.
// //


+ 4
- 0
github/repos_statuses.go View File

@ -30,6 +30,10 @@ type RepoStatus struct {
UpdatedAt *time.Time `json:"updated_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"`
} }
func (r *RepoStatus) String() string {
return Stringify(r)
}
// ListStatuses lists the statuses of a repository at the specified // ListStatuses lists the statuses of a repository at the specified
// reference. ref can be a SHA, a branch name, or a tag name. // reference. ref can be a SHA, a branch name, or a tag name.
// //


+ 4
- 0
github/search.go View File

@ -102,6 +102,10 @@ type CodeResult struct {
Repository *Repository `json:"repository,omitempty"` Repository *Repository `json:"repository,omitempty"`
} }
func (c *CodeResult) String() string {
return Stringify(c)
}
// Code searches code via various criteria. // Code searches code via various criteria.
// //
// GitHub API docs: http://developer.github.com/v3/search/#search-code // GitHub API docs: http://developer.github.com/v3/search/#search-code


+ 93
- 0
github/strings.go View File

@ -0,0 +1,93 @@
// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"bytes"
"fmt"
"io"
"reflect"
)
var timestampType = reflect.TypeOf(Timestamp{})
// Stringify attempts to create a reasonable string representation of types in
// the GitHub library. It does things like resolve pointers to their values
// and omits struct fields with nil values.
func Stringify(message interface{}) string {
var buf bytes.Buffer
v := reflect.ValueOf(message)
stringifyValue(&buf, v)
return buf.String()
}
// stringifyValue was heavily inspired by the goprotobuf library.
func stringifyValue(w io.Writer, val reflect.Value) {
if val.Kind() == reflect.Ptr && val.IsNil() {
w.Write([]byte("<nil>"))
return
}
v := reflect.Indirect(val)
switch v.Kind() {
case reflect.String:
fmt.Fprintf(w, `"%s"`, v)
case reflect.Slice:
w.Write([]byte{'['})
for i := 0; i < v.Len(); i++ {
if i > 0 {
w.Write([]byte{' '})
}
stringifyValue(w, v.Index(i))
}
w.Write([]byte{']'})
return
case reflect.Struct:
if v.Type().Name() != "" {
w.Write([]byte(v.Type().String()))
}
// special handling of Timestamp values
if v.Type() == timestampType {
fmt.Fprint(w, v.Interface())
return
}
w.Write([]byte{'{'})
var sep bool
for i := 0; i < v.NumField(); i++ {
fv := v.Field(i)
if fv.Kind() == reflect.Ptr && fv.IsNil() {
continue
}
if fv.Kind() == reflect.Slice && fv.IsNil() {
continue
}
if sep {
w.Write([]byte(", "))
} else {
sep = true
}
w.Write([]byte(v.Type().Field(i).Name))
w.Write([]byte{':'})
stringifyValue(w, fv)
}
w.Write([]byte{'}'})
default:
if v.CanInterface() {
fmt.Fprint(w, v.Interface())
}
}
}

+ 75
- 0
github/strings_test.go View File

@ -0,0 +1,75 @@
// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"testing"
"time"
)
func TestStringify(t *testing.T) {
var nilPointer *string
var tests = []struct {
in interface{}
out string
}{
// basic types
{"foo", `"foo"`},
{123, `123`},
{1.5, `1.5`},
{false, `false`},
{
[]string{"a", "b"},
`["a" "b"]`,
},
{
struct {
A []string
}{nil},
// nil slice is skipped
`{}`,
},
{
struct {
A string
}{"foo"},
// structs not of a named type get no prefix
`{A:"foo"}`,
},
// pointers
{nilPointer, `<nil>`},
{String("foo"), `"foo"`},
{Int(123), `123`},
{Bool(false), `false`},
{
[]*string{String("a"), String("b")},
`["a" "b"]`,
},
// actual GitHub structs
{
Timestamp{time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)},
`github.Timestamp{2006-01-02 15:04:05 +0000 UTC}`,
},
{
User{ID: Int(123), Name: String("n")},
`github.User{ID:123, Name:"n"}`,
},
{
Repository{Owner: &User{ID: Int(123)}},
`github.Repository{Owner:github.User{ID:123}}`,
},
}
for i, tt := range tests {
s := Stringify(tt.in)
if s != tt.out {
t.Errorf("%d. Stringify(%q) => %q, want %q", i, tt.in, s, tt.out)
}
}
}

+ 4
- 0
github/timestamp.go View File

@ -18,6 +18,10 @@ type Timestamp struct {
time.Time time.Time
} }
func (t *Timestamp) String() string {
return t.Time.String()
}
// UnmarshalJSON implements the json.Unmarshaler interface. // UnmarshalJSON implements the json.Unmarshaler interface.
// Time is expected in RFC3339 or Unix format. // Time is expected in RFC3339 or Unix format.
func (t *Timestamp) UnmarshalJSON(data []byte) (err error) { func (t *Timestamp) UnmarshalJSON(data []byte) (err error) {


+ 4
- 0
github/users.go View File

@ -39,6 +39,10 @@ type User struct {
CreatedAt *time.Time `json:"created_at,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"`
} }
func (u *User) String() string {
return Stringify(u)
}
// Get fetches a user. Passing the empty string will fetch the authenticated // Get fetches a user. Passing the empty string will fetch the authenticated
// user. // user.
// //


Loading…
Cancel
Save