1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
157 package equal
158
159 import (
160 "github.com/gogo/protobuf/gogoproto"
161 "github.com/gogo/protobuf/proto"
162 descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
163 "github.com/gogo/protobuf/protoc-gen-gogo/generator"
164 "github.com/gogo/protobuf/vanity"
165 )
166
167 type plugin struct {
168 *generator.Generator
169 generator.PluginImports
170 fmtPkg generator.Single
171 bytesPkg generator.Single
172 protoPkg generator.Single
173 }
174
175 func NewPlugin() *plugin {
176 return &plugin{}
177 }
178
179 func (p *plugin) Name() string {
180 return "equal"
181 }
182
183 func (p *plugin) Init(g *generator.Generator) {
184 p.Generator = g
185 }
186
187 func (p *plugin) Generate(file *generator.FileDescriptor) {
188 p.PluginImports = generator.NewPluginImports(p.Generator)
189 p.fmtPkg = p.NewImport("fmt")
190 p.bytesPkg = p.NewImport("bytes")
191 p.protoPkg = p.NewImport("github.com/gogo/protobuf/proto")
192
193 for _, msg := range file.Messages() {
194 if msg.DescriptorProto.GetOptions().GetMapEntry() {
195 continue
196 }
197 if gogoproto.HasVerboseEqual(file.FileDescriptorProto, msg.DescriptorProto) {
198 p.generateMessage(file, msg, true)
199 }
200 if gogoproto.HasEqual(file.FileDescriptorProto, msg.DescriptorProto) {
201 p.generateMessage(file, msg, false)
202 }
203 }
204 }
205
206 func (p *plugin) generateNullableField(fieldname string, verbose bool) {
207 p.P(`if this.`, fieldname, ` != nil && that1.`, fieldname, ` != nil {`)
208 p.In()
209 p.P(`if *this.`, fieldname, ` != *that1.`, fieldname, `{`)
210 p.In()
211 if verbose {
212 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", *this.`, fieldname, `, *that1.`, fieldname, `)`)
213 } else {
214 p.P(`return false`)
215 }
216 p.Out()
217 p.P(`}`)
218 p.Out()
219 p.P(`} else if this.`, fieldname, ` != nil {`)
220 p.In()
221 if verbose {
222 p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` == nil && that.`, fieldname, ` != nil")`)
223 } else {
224 p.P(`return false`)
225 }
226 p.Out()
227 p.P(`} else if that1.`, fieldname, ` != nil {`)
228 }
229
230 func (p *plugin) generateMsgNullAndTypeCheck(ccTypeName string, verbose bool) {
231 p.P(`if that == nil {`)
232 p.In()
233 if verbose {
234 p.P(`if this == nil {`)
235 p.In()
236 p.P(`return nil`)
237 p.Out()
238 p.P(`}`)
239 p.P(`return `, p.fmtPkg.Use(), `.Errorf("that == nil && this != nil")`)
240 } else {
241 p.P(`return this == nil`)
242 }
243 p.Out()
244 p.P(`}`)
245 p.P(``)
246 p.P(`that1, ok := that.(*`, ccTypeName, `)`)
247 p.P(`if !ok {`)
248 p.In()
249 p.P(`that2, ok := that.(`, ccTypeName, `)`)
250 p.P(`if ok {`)
251 p.In()
252 p.P(`that1 = &that2`)
253 p.Out()
254 p.P(`} else {`)
255 p.In()
256 if verbose {
257 p.P(`return `, p.fmtPkg.Use(), `.Errorf("that is not of type *`, ccTypeName, `")`)
258 } else {
259 p.P(`return false`)
260 }
261 p.Out()
262 p.P(`}`)
263 p.Out()
264 p.P(`}`)
265 p.P(`if that1 == nil {`)
266 p.In()
267 if verbose {
268 p.P(`if this == nil {`)
269 p.In()
270 p.P(`return nil`)
271 p.Out()
272 p.P(`}`)
273 p.P(`return `, p.fmtPkg.Use(), `.Errorf("that is type *`, ccTypeName, ` but is nil && this != nil")`)
274 } else {
275 p.P(`return this == nil`)
276 }
277 p.Out()
278 p.P(`} else if this == nil {`)
279 p.In()
280 if verbose {
281 p.P(`return `, p.fmtPkg.Use(), `.Errorf("that is type *`, ccTypeName, ` but is not nil && this == nil")`)
282 } else {
283 p.P(`return false`)
284 }
285 p.Out()
286 p.P(`}`)
287 }
288
289 func (p *plugin) generateField(file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto, verbose bool) {
290 proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
291 fieldname := p.GetOneOfFieldName(message, field)
292 repeated := field.IsRepeated()
293 ctype := gogoproto.IsCustomType(field)
294 nullable := gogoproto.IsNullable(field)
295 isNormal := (gogoproto.IsStdDuration(field) ||
296 gogoproto.IsStdDouble(field) ||
297 gogoproto.IsStdFloat(field) ||
298 gogoproto.IsStdInt64(field) ||
299 gogoproto.IsStdUInt64(field) ||
300 gogoproto.IsStdInt32(field) ||
301 gogoproto.IsStdUInt32(field) ||
302 gogoproto.IsStdBool(field) ||
303 gogoproto.IsStdString(field))
304 isBytes := gogoproto.IsStdBytes(field)
305 isTimestamp := gogoproto.IsStdTime(field)
306
307 if !repeated {
308 if ctype || isTimestamp {
309 if nullable {
310 p.P(`if that1.`, fieldname, ` == nil {`)
311 p.In()
312 p.P(`if this.`, fieldname, ` != nil {`)
313 p.In()
314 if verbose {
315 p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` != nil && that1.`, fieldname, ` == nil")`)
316 } else {
317 p.P(`return false`)
318 }
319 p.Out()
320 p.P(`}`)
321 p.Out()
322 p.P(`} else if !this.`, fieldname, `.Equal(*that1.`, fieldname, `) {`)
323 } else {
324 p.P(`if !this.`, fieldname, `.Equal(that1.`, fieldname, `) {`)
325 }
326 p.In()
327 if verbose {
328 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
329 } else {
330 p.P(`return false`)
331 }
332 p.Out()
333 p.P(`}`)
334 } else if isNormal {
335 if nullable {
336 p.generateNullableField(fieldname, verbose)
337 } else {
338 p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
339 }
340 p.In()
341 if verbose {
342 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
343 } else {
344 p.P(`return false`)
345 }
346 p.Out()
347 p.P(`}`)
348 } else if isBytes {
349 if nullable {
350 p.P(`if that1.`, fieldname, ` == nil {`)
351 p.In()
352 p.P(`if this.`, fieldname, ` != nil {`)
353 p.In()
354 if verbose {
355 p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` != nil && that1.`, fieldname, ` == nil")`)
356 } else {
357 p.P(`return false`)
358 }
359 p.Out()
360 p.P(`}`)
361 p.Out()
362 p.P(`} else if !`, p.bytesPkg.Use(), `.Equal(*this.`, fieldname, `, *that1.`, fieldname, `) {`)
363 } else {
364 p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
365 }
366 p.In()
367 if verbose {
368 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
369 } else {
370 p.P(`return false`)
371 }
372 p.Out()
373 p.P(`}`)
374 } else {
375 if field.IsMessage() || p.IsGroup(field) {
376 if nullable {
377 p.P(`if !this.`, fieldname, `.Equal(that1.`, fieldname, `) {`)
378 } else {
379 p.P(`if !this.`, fieldname, `.Equal(&that1.`, fieldname, `) {`)
380 }
381 } else if field.IsBytes() {
382 p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
383 } else if field.IsString() {
384 if nullable && !proto3 {
385 p.generateNullableField(fieldname, verbose)
386 } else {
387 p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
388 }
389 } else {
390 if nullable && !proto3 {
391 p.generateNullableField(fieldname, verbose)
392 } else {
393 p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
394 }
395 }
396 p.In()
397 if verbose {
398 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
399 } else {
400 p.P(`return false`)
401 }
402 p.Out()
403 p.P(`}`)
404 }
405 } else {
406 p.P(`if len(this.`, fieldname, `) != len(that1.`, fieldname, `) {`)
407 p.In()
408 if verbose {
409 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", len(this.`, fieldname, `), len(that1.`, fieldname, `))`)
410 } else {
411 p.P(`return false`)
412 }
413 p.Out()
414 p.P(`}`)
415 p.P(`for i := range this.`, fieldname, ` {`)
416 p.In()
417 if ctype && !p.IsMap(field) {
418 p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
419 } else if isTimestamp {
420 if nullable {
421 p.P(`if !this.`, fieldname, `[i].Equal(*that1.`, fieldname, `[i]) {`)
422 } else {
423 p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
424 }
425 } else if isNormal {
426 if nullable {
427 p.P(`if dthis, dthat := this.`, fieldname, `[i], that1.`, fieldname, `[i]; (dthis != nil && dthat != nil && *dthis != *dthat) || (dthis != nil && dthat == nil) || (dthis == nil && dthat != nil) {`)
428 } else {
429 p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
430 }
431 } else if isBytes {
432 if nullable {
433 p.P(`if !`, p.bytesPkg.Use(), `.Equal(*this.`, fieldname, `[i], *that1.`, fieldname, `[i]) {`)
434 } else {
435 p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `[i], that1.`, fieldname, `[i]) {`)
436 }
437 } else {
438 if p.IsMap(field) {
439 m := p.GoMapType(nil, field)
440 valuegoTyp, _ := p.GoType(nil, m.ValueField)
441 valuegoAliasTyp, _ := p.GoType(nil, m.ValueAliasField)
442 nullable, valuegoTyp, valuegoAliasTyp = generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
443
444 mapValue := m.ValueAliasField
445 mapValueNormal := (gogoproto.IsStdDuration(mapValue) ||
446 gogoproto.IsStdDouble(mapValue) ||
447 gogoproto.IsStdFloat(mapValue) ||
448 gogoproto.IsStdInt64(mapValue) ||
449 gogoproto.IsStdUInt64(mapValue) ||
450 gogoproto.IsStdInt32(mapValue) ||
451 gogoproto.IsStdUInt32(mapValue) ||
452 gogoproto.IsStdBool(mapValue) ||
453 gogoproto.IsStdString(mapValue))
454 mapValueBytes := gogoproto.IsStdBytes(mapValue)
455 if mapValue.IsMessage() || p.IsGroup(mapValue) {
456 if nullable && valuegoTyp == valuegoAliasTyp {
457 p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
458 } else {
459
460 a := `this.` + fieldname + `[i]`
461 b := `that1.` + fieldname + `[i]`
462 if !mapValueNormal && !mapValueBytes && valuegoTyp != valuegoAliasTyp {
463
464 a = `(` + valuegoTyp + `)(` + a + `)`
465 b = `(` + valuegoTyp + `)(` + b + `)`
466 }
467 p.P(`a := `, a)
468 p.P(`b := `, b)
469 if mapValueNormal {
470 if nullable {
471 p.P(`if *a != *b {`)
472 } else {
473 p.P(`if a != b {`)
474 }
475 } else if mapValueBytes {
476 if nullable {
477 p.P(`if !`, p.bytesPkg.Use(), `.Equal(*a, *b) {`)
478 } else {
479 p.P(`if !`, p.bytesPkg.Use(), `.Equal(a, b) {`)
480 }
481 } else if nullable {
482 p.P(`if !a.Equal(b) {`)
483 } else {
484 p.P(`if !(&a).Equal(&b) {`)
485 }
486 }
487 } else if mapValue.IsBytes() {
488 if ctype {
489 if nullable {
490 p.P(`if !this.`, fieldname, `[i].Equal(*that1.`, fieldname, `[i]) { //nullable`)
491 } else {
492 p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) { //not nullable`)
493 }
494 } else {
495 p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `[i], that1.`, fieldname, `[i]) {`)
496 }
497 } else if mapValue.IsString() {
498 p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
499 } else {
500 p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
501 }
502 } else if field.IsMessage() || p.IsGroup(field) {
503 if nullable {
504 p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
505 } else {
506 p.P(`if !this.`, fieldname, `[i].Equal(&that1.`, fieldname, `[i]) {`)
507 }
508 } else if field.IsBytes() {
509 p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `[i], that1.`, fieldname, `[i]) {`)
510 } else if field.IsString() {
511 p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
512 } else {
513 p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
514 }
515 }
516 p.In()
517 if verbose {
518 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this[%v](%v) Not Equal that[%v](%v)", i, this.`, fieldname, `[i], i, that1.`, fieldname, `[i])`)
519 } else {
520 p.P(`return false`)
521 }
522 p.Out()
523 p.P(`}`)
524 p.Out()
525 p.P(`}`)
526 }
527 }
528
529 func (p *plugin) generateMessage(file *generator.FileDescriptor, message *generator.Descriptor, verbose bool) {
530 ccTypeName := generator.CamelCaseSlice(message.TypeName())
531 if verbose {
532 p.P(`func (this *`, ccTypeName, `) VerboseEqual(that interface{}) error {`)
533 } else {
534 p.P(`func (this *`, ccTypeName, `) Equal(that interface{}) bool {`)
535 }
536 p.In()
537 p.generateMsgNullAndTypeCheck(ccTypeName, verbose)
538 oneofs := make(map[string]struct{})
539
540 for _, field := range message.Field {
541 oneof := field.OneofIndex != nil
542 if oneof {
543 fieldname := p.GetFieldName(message, field)
544 if _, ok := oneofs[fieldname]; ok {
545 continue
546 } else {
547 oneofs[fieldname] = struct{}{}
548 }
549 p.P(`if that1.`, fieldname, ` == nil {`)
550 p.In()
551 p.P(`if this.`, fieldname, ` != nil {`)
552 p.In()
553 if verbose {
554 p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` != nil && that1.`, fieldname, ` == nil")`)
555 } else {
556 p.P(`return false`)
557 }
558 p.Out()
559 p.P(`}`)
560 p.Out()
561 p.P(`} else if this.`, fieldname, ` == nil {`)
562 p.In()
563 if verbose {
564 p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` == nil && that1.`, fieldname, ` != nil")`)
565 } else {
566 p.P(`return false`)
567 }
568 p.Out()
569 if verbose {
570 p.P(`} else if err := this.`, fieldname, `.VerboseEqual(that1.`, fieldname, `); err != nil {`)
571 } else {
572 p.P(`} else if !this.`, fieldname, `.Equal(that1.`, fieldname, `) {`)
573 }
574 p.In()
575 if verbose {
576 p.P(`return err`)
577 } else {
578 p.P(`return false`)
579 }
580 p.Out()
581 p.P(`}`)
582 } else {
583 p.generateField(file, message, field, verbose)
584 }
585 }
586 if message.DescriptorProto.HasExtension() {
587 if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
588 fieldname := "XXX_InternalExtensions"
589 p.P(`thismap := `, p.protoPkg.Use(), `.GetUnsafeExtensionsMap(this)`)
590 p.P(`thatmap := `, p.protoPkg.Use(), `.GetUnsafeExtensionsMap(that1)`)
591 p.P(`for k, v := range thismap {`)
592 p.In()
593 p.P(`if v2, ok := thatmap[k]; ok {`)
594 p.In()
595 p.P(`if !v.Equal(&v2) {`)
596 p.In()
597 if verbose {
598 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this[%v](%v) Not Equal that[%v](%v)", k, thismap[k], k, thatmap[k])`)
599 } else {
600 p.P(`return false`)
601 }
602 p.Out()
603 p.P(`}`)
604 p.Out()
605 p.P(`} else {`)
606 p.In()
607 if verbose {
608 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, `[%v] Not In that", k)`)
609 } else {
610 p.P(`return false`)
611 }
612 p.Out()
613 p.P(`}`)
614 p.Out()
615 p.P(`}`)
616
617 p.P(`for k, _ := range thatmap {`)
618 p.In()
619 p.P(`if _, ok := thismap[k]; !ok {`)
620 p.In()
621 if verbose {
622 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, `[%v] Not In this", k)`)
623 } else {
624 p.P(`return false`)
625 }
626 p.Out()
627 p.P(`}`)
628 p.Out()
629 p.P(`}`)
630 } else {
631 fieldname := "XXX_extensions"
632 p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
633 p.In()
634 if verbose {
635 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
636 } else {
637 p.P(`return false`)
638 }
639 p.Out()
640 p.P(`}`)
641 }
642 }
643 if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
644 fieldname := "XXX_unrecognized"
645 p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
646 p.In()
647 if verbose {
648 p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
649 } else {
650 p.P(`return false`)
651 }
652 p.Out()
653 p.P(`}`)
654 }
655 if verbose {
656 p.P(`return nil`)
657 } else {
658 p.P(`return true`)
659 }
660 p.Out()
661 p.P(`}`)
662
663
664 m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
665 for _, field := range m.Field {
666 oneof := field.OneofIndex != nil
667 if !oneof {
668 continue
669 }
670 ccTypeName := p.OneOfTypeName(message, field)
671 if verbose {
672 p.P(`func (this *`, ccTypeName, `) VerboseEqual(that interface{}) error {`)
673 } else {
674 p.P(`func (this *`, ccTypeName, `) Equal(that interface{}) bool {`)
675 }
676 p.In()
677
678 p.generateMsgNullAndTypeCheck(ccTypeName, verbose)
679 vanity.TurnOffNullableForNativeTypes(field)
680 p.generateField(file, message, field, verbose)
681
682 if verbose {
683 p.P(`return nil`)
684 } else {
685 p.P(`return true`)
686 }
687 p.Out()
688 p.P(`}`)
689 }
690 }
691
692 func init() {
693 generator.RegisterPlugin(NewPlugin())
694 }
695
View as plain text