github.com/micro/go-micro/v2@v2.9.1/server/subscriber.go (about)

     1  package server
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	"github.com/micro/go-micro/v2/registry"
     8  )
     9  
    10  const (
    11  	subSig = "func(context.Context, interface{}) error"
    12  )
    13  
    14  type handler struct {
    15  	method  reflect.Value
    16  	reqType reflect.Type
    17  	ctxType reflect.Type
    18  }
    19  
    20  type subscriber struct {
    21  	topic      string
    22  	rcvr       reflect.Value
    23  	typ        reflect.Type
    24  	subscriber interface{}
    25  	handlers   []*handler
    26  	endpoints  []*registry.Endpoint
    27  	opts       SubscriberOptions
    28  }
    29  
    30  func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subscriber {
    31  	options := SubscriberOptions{
    32  		AutoAck: true,
    33  	}
    34  
    35  	for _, o := range opts {
    36  		o(&options)
    37  	}
    38  
    39  	var endpoints []*registry.Endpoint
    40  	var handlers []*handler
    41  
    42  	if typ := reflect.TypeOf(sub); typ.Kind() == reflect.Func {
    43  		h := &handler{
    44  			method: reflect.ValueOf(sub),
    45  		}
    46  
    47  		switch typ.NumIn() {
    48  		case 1:
    49  			h.reqType = typ.In(0)
    50  		case 2:
    51  			h.ctxType = typ.In(0)
    52  			h.reqType = typ.In(1)
    53  		}
    54  
    55  		handlers = append(handlers, h)
    56  
    57  		endpoints = append(endpoints, &registry.Endpoint{
    58  			Name:    "Func",
    59  			Request: extractSubValue(typ),
    60  			Metadata: map[string]string{
    61  				"topic":      topic,
    62  				"subscriber": "true",
    63  			},
    64  		})
    65  	} else {
    66  		hdlr := reflect.ValueOf(sub)
    67  		name := reflect.Indirect(hdlr).Type().Name()
    68  
    69  		for m := 0; m < typ.NumMethod(); m++ {
    70  			method := typ.Method(m)
    71  			h := &handler{
    72  				method: method.Func,
    73  			}
    74  
    75  			switch method.Type.NumIn() {
    76  			case 2:
    77  				h.reqType = method.Type.In(1)
    78  			case 3:
    79  				h.ctxType = method.Type.In(1)
    80  				h.reqType = method.Type.In(2)
    81  			}
    82  
    83  			handlers = append(handlers, h)
    84  
    85  			endpoints = append(endpoints, &registry.Endpoint{
    86  				Name:    name + "." + method.Name,
    87  				Request: extractSubValue(method.Type),
    88  				Metadata: map[string]string{
    89  					"topic":      topic,
    90  					"subscriber": "true",
    91  				},
    92  			})
    93  		}
    94  	}
    95  
    96  	return &subscriber{
    97  		rcvr:       reflect.ValueOf(sub),
    98  		typ:        reflect.TypeOf(sub),
    99  		topic:      topic,
   100  		subscriber: sub,
   101  		handlers:   handlers,
   102  		endpoints:  endpoints,
   103  		opts:       options,
   104  	}
   105  }
   106  
   107  func validateSubscriber(sub Subscriber) error {
   108  	typ := reflect.TypeOf(sub.Subscriber())
   109  	var argType reflect.Type
   110  
   111  	if typ.Kind() == reflect.Func {
   112  		name := "Func"
   113  		switch typ.NumIn() {
   114  		case 2:
   115  			argType = typ.In(1)
   116  		default:
   117  			return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s", name, typ.NumIn(), subSig)
   118  		}
   119  		if !isExportedOrBuiltinType(argType) {
   120  			return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType)
   121  		}
   122  		if typ.NumOut() != 1 {
   123  			return fmt.Errorf("subscriber %v has wrong number of outs: %v require signature %s",
   124  				name, typ.NumOut(), subSig)
   125  		}
   126  		if returnType := typ.Out(0); returnType != typeOfError {
   127  			return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String())
   128  		}
   129  	} else {
   130  		hdlr := reflect.ValueOf(sub.Subscriber())
   131  		name := reflect.Indirect(hdlr).Type().Name()
   132  
   133  		for m := 0; m < typ.NumMethod(); m++ {
   134  			method := typ.Method(m)
   135  
   136  			switch method.Type.NumIn() {
   137  			case 3:
   138  				argType = method.Type.In(2)
   139  			default:
   140  				return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s",
   141  					name, method.Name, method.Type.NumIn(), subSig)
   142  			}
   143  
   144  			if !isExportedOrBuiltinType(argType) {
   145  				return fmt.Errorf("%v argument type not exported: %v", name, argType)
   146  			}
   147  			if method.Type.NumOut() != 1 {
   148  				return fmt.Errorf(
   149  					"subscriber %v.%v has wrong number of outs: %v require signature %s",
   150  					name, method.Name, method.Type.NumOut(), subSig)
   151  			}
   152  			if returnType := method.Type.Out(0); returnType != typeOfError {
   153  				return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String())
   154  			}
   155  		}
   156  	}
   157  
   158  	return nil
   159  }
   160  
   161  func (s *subscriber) Topic() string {
   162  	return s.topic
   163  }
   164  
   165  func (s *subscriber) Subscriber() interface{} {
   166  	return s.subscriber
   167  }
   168  
   169  func (s *subscriber) Endpoints() []*registry.Endpoint {
   170  	return s.endpoints
   171  }
   172  
   173  func (s *subscriber) Options() SubscriberOptions {
   174  	return s.opts
   175  }