Skip to content

Commit 8f77def

Browse files
committed
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.
1 parent 3072d06 commit 8f77def

22 files changed

+275
-3
lines changed

examples/example.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func main() {
2121
if err != nil {
2222
fmt.Printf("error: %v\n\n", err)
2323
} else {
24-
fmt.Printf("%#v\n\n", repos)
24+
fmt.Printf("%v\n\n", github.Stringify(repos))
2525
}
2626

2727
rate, _, err := client.RateLimit()

github/activity_events.go

+12
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ type Event struct {
2525
ID *string `json:"id,omitempty"`
2626
}
2727

28+
func (e *Event) String() string {
29+
return Stringify(e)
30+
}
31+
2832
// Payload returns the parsed event payload. For recognized event types
2933
// (PushEvent), a value of the corresponding struct type will be returned.
3034
func (e *Event) Payload() (payload interface{}) {
@@ -49,6 +53,10 @@ type PushEvent struct {
4953
Commits []PushEventCommit `json:"commits,omitempty"`
5054
}
5155

56+
func (p *PushEvent) String() string {
57+
return Stringify(p)
58+
}
59+
5260
// PushEventCommit represents a git commit in a GitHub PushEvent.
5361
type PushEventCommit struct {
5462
SHA *string `json:"sha,omitempty"`
@@ -58,6 +66,10 @@ type PushEventCommit struct {
5866
Distinct *bool `json:"distinct"`
5967
}
6068

69+
func (p *PushEventCommit) String() string {
70+
return Stringify(p)
71+
}
72+
6173
// List public events.
6274
//
6375
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-public-events

github/gists.go

+8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ type Gist struct {
3333
CreatedAt *time.Time `json:"created_at,omitempty"`
3434
}
3535

36+
func (g *Gist) String() string {
37+
return Stringify(g)
38+
}
39+
3640
// GistFilename represents filename on a gist.
3741
type GistFilename string
3842

@@ -44,6 +48,10 @@ type GistFile struct {
4448
Content *string `json:"content,omitempty"`
4549
}
4650

51+
func (g *GistFile) String() string {
52+
return Stringify(g)
53+
}
54+
4755
// GistListOptions specifies the optional parameters to the
4856
// GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods.
4957
type GistListOptions struct {

github/gists_comments.go

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ type GistComment struct {
1919
CreatedAt *time.Time `json:"created_at,omitempty"`
2020
}
2121

22+
func (g *GistComment) String() string {
23+
return Stringify(g)
24+
}
25+
2226
// ListComments lists all comments for a gist.
2327
//
2428
// GitHub API docs: http://developer.github.com/v3/gists/comments/#list-comments-on-a-gist

github/git_commits.go

+8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ type Commit struct {
2020
Parents []Commit `json:"parents,omitempty"`
2121
}
2222

23+
func (c *Commit) String() string {
24+
return Stringify(c)
25+
}
26+
2327
// CommitAuthor represents the author or committer of a commit. The commit
2428
// author may not correspond to a GitHub User.
2529
type CommitAuthor struct {
@@ -28,6 +32,10 @@ type CommitAuthor struct {
2832
Email *string `json:"email,omitempty"`
2933
}
3034

35+
func (c *CommitAuthor) String() string {
36+
return Stringify(c)
37+
}
38+
3139
// GetCommit fetchs the Commit object for a given SHA.
3240
//
3341
// GitHub API docs: http://developer.github.com/v3/git/commits/#get-a-commit

github/git_trees.go

+8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ type Tree struct {
1313
Entries []TreeEntry `json:"tree,omitempty"`
1414
}
1515

16+
func (t *Tree) String() string {
17+
return Stringify(t)
18+
}
19+
1620
// TreeEntry represents the contents of a tree structure. TreeEntry can
1721
// represent either a blob, a commit (in the case of a submodule), or another
1822
// tree.
@@ -24,6 +28,10 @@ type TreeEntry struct {
2428
Size *int `json:"size,omitempty"`
2529
}
2630

31+
func (t *TreeEntry) String() string {
32+
return Stringify(t)
33+
}
34+
2735
// GetTree fetches the Tree object for a given sha hash from a repository.
2836
//
2937
// GitHub API docs: http://developer.github.com/v3/git/trees/#get-a-tree

github/issues.go

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ type Issue struct {
3838
// TODO(willnorris): milestone
3939
}
4040

41+
func (i *Issue) String() string {
42+
return Stringify(i)
43+
}
44+
4145
// IssueListOptions specifies the optional parameters to the IssuesService.List
4246
// and IssuesService.ListByOrg methods.
4347
type IssueListOptions struct {

github/issues_comments.go

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ type IssueComment struct {
2020
UpdatedAt *time.Time `json:"updated_at,omitempty"`
2121
}
2222

23+
func (i *IssueComment) String() string {
24+
return Stringify(i)
25+
}
26+
2327
// IssueListCommentsOptions specifies the optional parameters to the
2428
// IssuesService.ListComments method.
2529
type IssueListCommentsOptions struct {

github/issues_labels.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ type Label struct {
1414
Color *string `json:"color,omitempty"`
1515
}
1616

17-
func (label Label) String() string {
18-
return fmt.Sprint(label.Name)
17+
func (l Label) String() string {
18+
return fmt.Sprint(*l.Name)
1919
}
2020

2121
// ListLabels lists all labels for a repository.

github/orgs.go

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ type Organization struct {
3030
CreatedAt *time.Time `json:"created_at,omitempty"`
3131
}
3232

33+
func (o *Organization) String() string {
34+
return Stringify(o)
35+
}
36+
3337
// List the organizations for a user. Passing the empty string will list
3438
// organizations for the authenticated user.
3539
//

github/orgs_teams.go

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ type Team struct {
1919
ReposCount *int `json:"repos_count,omitempty"`
2020
}
2121

22+
func (t *Team) String() string {
23+
return Stringify(t)
24+
}
25+
2226
// ListTeams lists all of the teams for an organization.
2327
//
2428
// GitHub API docs: http://developer.github.com/v3/orgs/teams/#list-teams

github/pulls.go

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ type PullRequest struct {
4242
// TODO(willnorris): add head and base once we have a Commit struct defined somewhere
4343
}
4444

45+
func (p *PullRequest) String() string {
46+
return Stringify(p)
47+
}
48+
4549
// PullRequestListOptions specifies the optional parameters to the
4650
// PullRequestsService.List method.
4751
type PullRequestListOptions struct {

github/pulls_comments.go

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ type PullRequestComment struct {
2323
UpdatedAt *time.Time `json:"updated_at,omitempty"`
2424
}
2525

26+
func (p *PullRequestComment) String() string {
27+
return Stringify(p)
28+
}
29+
2630
// PullRequestListCommentsOptions specifies the optional parameters to the
2731
// PullRequestsService.ListComments method.
2832
type PullRequestListCommentsOptions struct {

github/repos.go

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ type Repository struct {
3434
HasWiki *bool `json:"has_wiki"`
3535
}
3636

37+
func (r *Repository) String() string {
38+
return Stringify(r)
39+
}
40+
3741
// RepositoryListOptions specifies the optional parameters to the
3842
// RepositoriesService.List method.
3943
type RepositoryListOptions struct {

github/repos_comments.go

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ type RepositoryComment struct {
2626
Position *int `json:"position,omitempty"`
2727
}
2828

29+
func (r *RepositoryComment) String() string {
30+
return Stringify(r)
31+
}
32+
2933
// ListComments lists all the comments for the repository.
3034
//
3135
// GitHub API docs: http://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository

github/repos_hooks.go

+16
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ type WebHookPayload struct {
3333
Repo *Repository `json:"repository,omitempty"`
3434
}
3535

36+
func (w *WebHookPayload) String() string {
37+
return Stringify(w)
38+
}
39+
3640
// WebHookCommit represents the commit variant we receive from GitHub in a
3741
// WebHookPayload.
3842
type WebHookCommit struct {
@@ -47,6 +51,10 @@ type WebHookCommit struct {
4751
Timestamp *time.Time `json:"timestamp,omitempty"`
4852
}
4953

54+
func (w *WebHookCommit) String() string {
55+
return Stringify(w)
56+
}
57+
5058
// WebHookAuthor represents the author or committer of a commit, as specified
5159
// in a WebHookCommit. The commit author may not correspond to a GitHub User.
5260
type WebHookAuthor struct {
@@ -55,6 +63,10 @@ type WebHookAuthor struct {
5563
Username *string `json:"username,omitempty"`
5664
}
5765

66+
func (w *WebHookAuthor) String() string {
67+
return Stringify(w)
68+
}
69+
5870
// Hook represents a GitHub (web and service) hook for a repository.
5971
type Hook struct {
6072
CreatedAt *time.Time `json:"created_at,omitempty"`
@@ -66,6 +78,10 @@ type Hook struct {
6678
ID *int `json:"id,omitempty"`
6779
}
6880

81+
func (h *Hook) String() string {
82+
return Stringify(h)
83+
}
84+
6985
// CreateHook creates a Hook for the specified repository.
7086
// Name and Config are required fields.
7187
//

github/repos_statuses.go

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ type RepoStatus struct {
3030
UpdatedAt *time.Time `json:"updated_at,omitempty"`
3131
}
3232

33+
func (r *RepoStatus) String() string {
34+
return Stringify(r)
35+
}
36+
3337
// ListStatuses lists the statuses of a repository at the specified
3438
// reference. ref can be a SHA, a branch name, or a tag name.
3539
//

github/search.go

+4
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ type CodeResult struct {
102102
Repository *Repository `json:"repository,omitempty"`
103103
}
104104

105+
func (c *CodeResult) String() string {
106+
return Stringify(c)
107+
}
108+
105109
// Code searches code via various criteria.
106110
//
107111
// GitHub API docs: http://developer.github.com/v3/search/#search-code

github/strings.go

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2013 The go-github AUTHORS. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style
4+
// license that can be found in the LICENSE file.
5+
6+
package github
7+
8+
import (
9+
"bytes"
10+
"fmt"
11+
"io"
12+
13+
"reflect"
14+
)
15+
16+
var timestampType = reflect.TypeOf(Timestamp{})
17+
18+
// Stringify attempts to create a reasonable string representation of types in
19+
// the GitHub library. It does things like resolve pointers to their values
20+
// and omits struct fields with nil values.
21+
func Stringify(message interface{}) string {
22+
var buf bytes.Buffer
23+
v := reflect.ValueOf(message)
24+
stringifyValue(&buf, v)
25+
return buf.String()
26+
}
27+
28+
// stringifyValue was heavily inspired by the goprotobuf library.
29+
30+
func stringifyValue(w io.Writer, val reflect.Value) {
31+
if val.Kind() == reflect.Ptr && val.IsNil() {
32+
w.Write([]byte("<nil>"))
33+
return
34+
}
35+
36+
v := reflect.Indirect(val)
37+
38+
switch v.Kind() {
39+
case reflect.String:
40+
fmt.Fprintf(w, `"%s"`, v)
41+
case reflect.Slice:
42+
w.Write([]byte{'['})
43+
for i := 0; i < v.Len(); i++ {
44+
if i > 0 {
45+
w.Write([]byte{' '})
46+
}
47+
48+
stringifyValue(w, v.Index(i))
49+
}
50+
51+
w.Write([]byte{']'})
52+
return
53+
case reflect.Struct:
54+
if v.Type().Name() != "" {
55+
w.Write([]byte(v.Type().String()))
56+
}
57+
58+
// special handling of Timestamp values
59+
if v.Type() == timestampType {
60+
fmt.Fprint(w, v.Interface())
61+
return
62+
}
63+
64+
w.Write([]byte{'{'})
65+
66+
var sep bool
67+
for i := 0; i < v.NumField(); i++ {
68+
fv := v.Field(i)
69+
if fv.Kind() == reflect.Ptr && fv.IsNil() {
70+
continue
71+
}
72+
if fv.Kind() == reflect.Slice && fv.IsNil() {
73+
continue
74+
}
75+
76+
if sep {
77+
w.Write([]byte(", "))
78+
} else {
79+
sep = true
80+
}
81+
82+
w.Write([]byte(v.Type().Field(i).Name))
83+
w.Write([]byte{':'})
84+
stringifyValue(w, fv)
85+
}
86+
87+
w.Write([]byte{'}'})
88+
default:
89+
if v.CanInterface() {
90+
fmt.Fprint(w, v.Interface())
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)