Repository: google/go-github (Go)

Issue:
PushEvent GetCommits() Method
I would like to use an interface for this event in a local implementation, but unfortunately a getter for this type doesn't exist. Can we create a getter for `PushEvent.GetCommits`?

```
type PushEvent struct {
...
Commits      []*HeadCommit
...
}

func (p *PushEvent) GetCommits() []*HeadCommit {
    return p.Commits
}
```


Discussion:
In general, getters are only provided for anything where a `nil` might be a problem (which is not the case for slices of anything), and all getters are auto-generated.

So if you want to add one for this, then I suppose we have a few options:

1. add getters for all fields, but that seems wasteful
2. modify the auto-generator to add a list of "special" getters that we want to always add (e.g. `PushEvent.Commits`)
3. manually add a getter to the main source code and completely ignore the auto-generator

I'm kinda leaning toward option 2.

Thoughts?

2 sounds good to me 😸 Let me take a look at the auto-generator code. Thank you!
I would like to propose some changes and request for a review. 
- https://github.com/google/go-github/pull/2776

I would appreciate it if you could check it out!



Patch:
diff --git a/github/gen-accessors.go b/github/gen-accessors.go
index b222465f39f..4bc22acdc15 100644
--- a/github/gen-accessors.go
+++ b/github/gen-accessors.go
@@ -51,6 +51,11 @@ var (
 	skipStructs = map[string]bool{
 		"Client": true,
 	}
+
+	// whitelistSliceGetters lists "struct.field" to add getter method
+	whitelistSliceGetters = map[string]bool{
+		"PushEvent.Commits": true,
+	}
 )
 
 func logf(fmt string, args ...interface{}) {
@@ -137,6 +142,12 @@ func (t *templateData) processAST(f *ast.File) error {
 					case *ast.MapType:
 						t.addMapType(x, ts.Name.String(), fieldName.String(), false)
 						continue
+					case *ast.ArrayType:
+						if key := fmt.Sprintf("%v.%v", ts.Name, fieldName); whitelistSliceGetters[key] {
+							logf("Method %v is whitelist; adding getter method.", key)
+							t.addArrayType(x, ts.Name.String(), fieldName.String(), false)
+							continue
+						}
 					}
 
 					logf("Skipping field type %T, fieldName=%v", field.Type, fieldName)
@@ -145,7 +156,7 @@ func (t *templateData) processAST(f *ast.File) error {
 
 				switch x := se.X.(type) {
 				case *ast.ArrayType:
-					t.addArrayType(x, ts.Name.String(), fieldName.String())
+					t.addArrayType(x, ts.Name.String(), fieldName.String(), true)
 				case *ast.Ident:
 					t.addIdent(x, ts.Name.String(), fieldName.String())
 				case *ast.MapType:
@@ -218,17 +229,26 @@ func newGetter(receiverType, fieldName, fieldType, zeroValue string, namedStruct
 	}
 }
 
-func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string) {
+func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string, isAPointer bool) {
 	var eltType string
+	var ng *getter
 	switch elt := x.Elt.(type) {
 	case *ast.Ident:
 		eltType = elt.String()
+		ng = newGetter(receiverType, fieldName, "[]"+eltType, "nil", false)
+	case *ast.StarExpr:
+		ident, ok := elt.X.(*ast.Ident)
+		if !ok {
+			return
+		}
+		ng = newGetter(receiverType, fieldName, "[]*"+ident.String(), "nil", false)
 	default:
 		logf("addArrayType: type %q, field %q: unknown elt type: %T %+v; skipping.", receiverType, fieldName, elt, elt)
 		return
 	}
 
-	t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil", false))
+	ng.ArrayType = !isAPointer
+	t.Getters = append(t.Getters, ng)
 }
 
 func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) {
@@ -322,6 +342,7 @@ type getter struct {
 	ZeroValue    string
 	NamedStruct  bool // Getter for named struct.
 	MapType      bool
+	ArrayType    bool
 }
 
 type byName []*getter
@@ -356,8 +377,8 @@ func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() *{{.FieldType}} {
   }
   return {{.ReceiverVar}}.{{.FieldName}}
 }
-{{else if .MapType}}
-// Get{{.FieldName}} returns the {{.FieldName}} map if it's non-nil, an empty map otherwise.
+{{else if or .MapType .ArrayType }}
+// Get{{.FieldName}} returns the {{.FieldName}} {{if .MapType}}map{{else if .ArrayType }}slice{{end}} if it's non-nil, {{if .MapType}}an empty map{{else if .ArrayType }}nil{{end}} otherwise.
 func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} {
   if {{.ReceiverVar}} == nil || {{.ReceiverVar}}.{{.FieldName}} == nil {
     return {{.ZeroValue}}
@@ -402,7 +423,7 @@ func Test{{.ReceiverType}}_Get{{.FieldName}}(tt *testing.T) {
   {{.ReceiverVar}} = nil
   {{.ReceiverVar}}.Get{{.FieldName}}()
 }
-{{else if .MapType}}
+{{else if or .MapType .ArrayType}}
 func Test{{.ReceiverType}}_Get{{.FieldName}}(tt *testing.T) {
   zeroValue := {{.FieldType}}{}
   {{.ReceiverVar}} := &{{.ReceiverType}}{ {{.FieldName}}: zeroValue }
diff --git a/github/github-accessors.go b/github/github-accessors.go
index 91743340536..087184e62ac 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -15054,6 +15054,14 @@ func (p *PushEvent) GetBefore() string {
 	return *p.Before
 }
 
+// GetCommits returns the Commits slice if it's non-nil, nil otherwise.
+func (p *PushEvent) GetCommits() []*HeadCommit {
+	if p == nil || p.Commits == nil {
+		return nil
+	}
+	return p.Commits
+}
+
 // GetCompare returns the Compare field if it's non-nil, zero value otherwise.
 func (p *PushEvent) GetCompare() string {
 	if p == nil || p.Compare == nil {


Test patch:
diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go
index 8f7292d7b04..7d1c3f17cce 100644
--- a/github/github-accessors_test.go
+++ b/github/github-accessors_test.go
@@ -17477,6 +17477,16 @@ func TestPushEvent_GetBefore(tt *testing.T) {
 	p.GetBefore()
 }
 
+func TestPushEvent_GetCommits(tt *testing.T) {
+	zeroValue := []*HeadCommit{}
+	p := &PushEvent{Commits: zeroValue}
+	p.GetCommits()
+	p = &PushEvent{}
+	p.GetCommits()
+	p = nil
+	p.GetCommits()
+}
+
 func TestPushEvent_GetCompare(tt *testing.T) {
 	var zeroValue string
 	p := &PushEvent{Compare: &zeroValue}