1
- // Copyright 2019 The Gitea Authors. All rights reserved.
1
+ // Copyright 2020 The Gitea Authors. All rights reserved.
2
2
// Use of this source code is governed by a MIT-style
3
3
// license that can be found in the LICENSE file.
4
4
@@ -19,64 +19,114 @@ import (
19
19
"time"
20
20
)
21
21
22
+ // funcDef are a semi-generic function definitions
22
23
type funcDef struct {
23
24
Name string
24
25
Args []funcDefArg
25
26
}
26
27
28
+ // funcDefArg describe an argument to a function
27
29
type funcDefArg struct {
28
30
Name string
29
31
Type string
30
32
}
31
33
34
+ // main will generate two files that implement the Notifier interface
35
+ // defined in notifier.go
36
+ //
37
+ // * the NullNotifier which is a basic Notifier that does nothing
38
+ // when each of its methods is called
39
+ //
40
+ // * the QueueNotifier which is a notifier that sends its commands down
41
+ // a queue.
42
+ //
43
+ // The main benefit of this generation is that we never need to keep these
44
+ // up to date again. Add a new function to Notifier and the NullNotifier and
45
+ // the NotifierQueue will gain these functions automatically.
46
+ //
47
+ // There are two caveat:
48
+ // * All notifier functions must not return anything.
49
+ // * If you add a new import you will need to add it to the templates below
32
50
func main () {
51
+
52
+ // OK build the AST from the notifier.go file
33
53
fset := token .NewFileSet () // positions are relative to fset
34
54
f , err := parser .ParseFile (fset , "notifier.go" , nil , 0 )
35
55
if err != nil {
36
56
panic (err )
37
57
}
58
+
59
+ // func will collect all the function definitions from the Notifier interface
38
60
funcs := make ([]funcDef , 0 )
39
- //currentFunc := funcDef{}
61
+
40
62
ast .Inspect (f , func (n ast.Node ) bool {
41
63
spec , ok := n .(* ast.TypeSpec )
42
- if ! ok || spec .Name .Name != "Notifier" {
43
- return true
64
+ if ! ok || spec .Name .Name != "Notifier" { // We only care about the Notifier interface declaration
65
+ return true // If we are not a type decl or aren't looking at the Notifier decl keep looking
44
66
}
67
+
68
+ // We're at: `type Notifier ...` so now we need check that it's an interface
45
69
child , ok := spec .Type .(* ast.InterfaceType )
46
70
if ! ok {
47
- return false
71
+ return false // There's no point looking in non interface types.
48
72
}
73
+
74
+ // OK we're in the declaration of the Notifier, e.g.
75
+ // type Notifier interface { ... }
76
+
77
+ // Let's look at each Method in turn, but first we redefine
78
+ // funcs now we know how big it's supposed to be
49
79
funcs = make ([]funcDef , len (child .Methods .List ))
50
80
for i , method := range child .Methods .List {
51
- methodFuncDef := method .Type .(* ast.FuncType )
81
+ // example: NotifyPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits)
82
+
83
+ // method here is looking at the NotifyPushCommits...
84
+
85
+ // We know that interfaces have FuncType for the method
86
+ methodFuncDef := method .Type .(* ast.FuncType ) // eg. (...)
87
+
88
+ // Extract the function definition from the method
52
89
def := funcDef {}
53
- def .Name = method .Names [0 ].Name
90
+ def .Name = method .Names [0 ].Name // methods only have one name in interfaces <- NotifyPushCommits
91
+
92
+ // Now construct the args
54
93
def .Args = make ([]funcDefArg , 0 , len (methodFuncDef .Params .List ))
55
94
for j , param := range methodFuncDef .Params .List {
95
+
96
+ // interfaces don't have to name their arguments e.g. NotifyNewIssue(*models.Issue)
97
+ // but we need a name to make a function call
56
98
defaultName := fmt .Sprintf ("unknown%d" , j )
99
+
100
+ // Now get the type - here we will just use what is used in source file. (See caveat 2.)
57
101
sb := strings.Builder {}
58
102
format .Node (& sb , fset , param .Type )
59
103
104
+ // If our parameter is unnamed
60
105
if len (param .Names ) == 0 {
61
106
def .Args = append (def .Args , funcDefArg {
62
107
Name : defaultName ,
63
108
Type : sb .String (),
64
109
})
65
110
} else {
111
+ // Now in the example NotifyPushCommits we see that refname, oldCommitID etc don't have type following them
112
+ // The AST creates these as a single param with mulitple names
113
+ // Therefore iterate through the param.Names
66
114
for _ , ident := range param .Names {
67
- def .Args = append (def .Args , funcDefArg {
68
- Name : ident .Name ,
69
- Type : sb .String (),
70
- })
115
+ def .Args = append (def .Args , funcDefArg {
116
+ Name : ident .Name ,
117
+ Type : sb .String (),
118
+ })
119
+ }
71
120
}
72
- }
73
121
}
74
122
funcs [i ] = def
75
123
}
76
124
77
- return true
125
+ // We're done so stop walking
126
+ return false
78
127
})
79
128
129
+ // First lets create the NullNotifier
80
130
buf := bytes.Buffer {}
81
131
nullTemplate .Execute (& buf , struct {
82
132
Timestamp time.Time
@@ -96,6 +146,7 @@ func main() {
96
146
panic (err )
97
147
}
98
148
149
+ // Then create the NotifierQueue
99
150
buf = bytes.Buffer {}
100
151
queueTemplate .Execute (& buf , struct {
101
152
Timestamp time.Time
@@ -132,22 +183,25 @@ import (
132
183
"code.gitea.io/gitea/modules/queue"
133
184
)
134
185
135
- // FunctionCall represents is function call with json.Marshaled arguments
186
+ // FunctionCall represents a function call with json.Marshaled arguments
136
187
type FunctionCall struct {
137
188
Name string
138
189
Args [][]byte
139
190
}
140
191
192
+ // QueueNotifier is a notifier queue
141
193
type QueueNotifier struct {
142
194
name string
143
195
notifiers []Notifier
144
196
internal queue.Queue
145
197
}
146
198
199
+ // Ensure that QueueNotifier fulfils the Notifier interface
147
200
var (
148
201
_ Notifier = &QueueNotifier{}
149
202
)
150
203
204
+ // NewQueueNotifier creates a notifier that queues notifications and on dequeueing sends them to the provided notifiers
151
205
func NewQueueNotifier(name string, notifiers []Notifier) Notifier {
152
206
q := &QueueNotifier{
153
207
name: name,
@@ -157,6 +211,7 @@ func NewQueueNotifier(name string, notifiers []Notifier) Notifier {
157
211
return q
158
212
}
159
213
214
+ // NewQueueNotifierWithHandle creates a notifier queue with a specific handler function
160
215
func NewQueueNotifierWithHandle(name string, handle queue.HandlerFunc) Notifier {
161
216
q := &QueueNotifier{
162
217
name: name,
@@ -235,6 +290,7 @@ import (
235
290
type NullNotifier struct {
236
291
}
237
292
293
+ // Ensure that NullNotifier fulfils the Notifier interface
238
294
var (
239
295
_ Notifier = &NullNotifier{}
240
296
)
0 commit comments