func variableArgsHandler(m reflect.Value) moleculer.ActionHandler {
	ptypes := getParamTypes(m)
	return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
		err := validateArgs(ptypes, p)
		if err != nil {
			return err
		}
		args := buildArgs(ptypes, p)
		return checkReturn(m.Call(args))
	}
}
variableArgsHandler creates an action hanler that deals with variable number of arguments.
package service
import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"github.com/moleculer-go/moleculer"
	"github.com/moleculer-go/moleculer/payload"
	log "github.com/sirupsen/logrus"
)
type Action struct {
	name     string
	fullname string
	handler  moleculer.ActionHandler
	params   moleculer.ActionSchema
}
type Event struct {
	name        string
	serviceName string
	group       string
	handler     moleculer.EventHandler
}
func (event *Event) Handler() moleculer.EventHandler {
	return event.handler
}
func (event *Event) Name() string {
	return event.name
}
func (event *Event) ServiceName() string {
	return event.serviceName
}
func (event *Event) Group() string {
	return event.group
}
type HasName interface {
	Name() string
}
type HasVersion interface {
	Version() string
}
type HasDependencies interface {
	Dependencies() []string
}
type HasSettings interface {
	Settings() map[string]interface{}
}
type HasMetadata interface {
	Metadata() map[string]interface{}
}
type HasMixins interface {
	Mixins() []moleculer.Mixin
}
type HasEvents interface {
	Events() []moleculer.Event
}
func ParseVersion(iver interface{}) string {
	v, ok := iver.(string)
	if ok {
		return v
	}
	f, ok := iver.(float64)
	if ok {
		return fmt.Sprintf("%g", f)
	}
	i, ok := iver.(int64)
	if ok {
		return fmt.Sprintf("%d", i)
	}
	return fmt.Sprintf("%v", iver)
}
type Service struct {
	nodeID       string
	fullname     string
	name         string
	version      string
	dependencies []string
	settings     map[string]interface{}
	metadata     map[string]interface{}
	actions      []Action
	events       []Event
	created      moleculer.CreatedFunc
	started      moleculer.LifecycleFunc
	stopped      moleculer.LifecycleFunc
	schema       *moleculer.ServiceSchema
	logger       *log.Entry
}
func (service *Service) Schema() *moleculer.ServiceSchema {
	return service.schema
}
func (service *Service) NodeID() string {
	return service.nodeID
}
func (service *Service) Settings() map[string]interface{} {
	return service.settings
}
func (service *Service) SetNodeID(nodeID string) {
	service.nodeID = nodeID
}
func (service *Service) Dependencies() []string {
	return service.dependencies
}
func (serviceAction *Action) Handler() moleculer.ActionHandler {
	return serviceAction.handler
}
func (serviceAction *Action) Name() string {
	return serviceAction.name
}
func (serviceAction *Action) FullName() string {
	return serviceAction.fullname
}
func (service *Service) Name() string {
	return service.name
}
func (service *Service) FullName() string {
	return service.fullname
}
func (service *Service) Version() string {
	return service.version
}
func (service *Service) Actions() []Action {
	return service.actions
}
func (service *Service) Summary() map[string]string {
	return map[string]string{
		"name":    service.name,
		"version": service.version,
		"nodeID":  service.nodeID,
	}
}
func (service *Service) Events() []Event {
	return service.events
}
func findAction(name string, actions []moleculer.Action) bool {
	for _, a := range actions {
		if a.Name == name {
			return true
		}
	}
	return false
}
func extendActions(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
	for _, ma := range mixin.Actions {
		if !findAction(ma.Name, service.Actions) {
			service.Actions = append(service.Actions, ma)
		}
	}
	return service
}
func mergeDependencies(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
	list := []string{}
	for _, item := range mixin.Dependencies {
		list = append(list, item)
	}
	for _, item := range service.Dependencies {
		list = append(list, item)
	}
	service.Dependencies = list
	return service
}
func concatenateEvents(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
	for _, mixinEvent := range mixin.Events {
		for _, serviceEvent := range service.Events {
			if serviceEvent.Name != mixinEvent.Name {
				service.Events = append(service.Events, mixinEvent)
			}
		}
	}
	return service
}
func MergeSettings(settings ...map[string]interface{}) map[string]interface{} {
	result := map[string]interface{}{}
	for _, set := range settings {
		if set != nil {
			for key, value := range set {
				result[key] = value
			}
		}
	}
	return result
}
func extendSettings(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
	service.Settings = MergeSettings(mixin.Settings, service.Settings)
	return service
}
func extendMetadata(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
	service.Metadata = MergeSettings(mixin.Metadata, service.Metadata)
	return service
}
func extendHooks(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
	service.Hooks = MergeSettings(mixin.Hooks, service.Hooks)
	return service
}
func chainCreated(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
	if mixin.Created != nil {
		svcHook := service.Created
		mixinHook := mixin.Created
		service.Created = func(svc moleculer.ServiceSchema, log *log.Entry) {
			mixinHook(svc, log)
			if svcHook != nil {
				svcHook(svc, log)
			}
		}
	}
	return service
}
func chainStarted(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
	if mixin.Started != nil {
		svcHook := service.Started
		mixinHook := mixin.Started
		service.Started = func(ctx moleculer.BrokerContext, svc moleculer.ServiceSchema) {
			mixinHook(ctx, svc)
			if svcHook != nil {
				svcHook(ctx, svc)
			}
		}
	}
	return service
}
func chainStopped(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
	if mixin.Stopped != nil {
		svcHook := service.Stopped
		mixinHook := mixin.Stopped
		service.Stopped = func(ctx moleculer.BrokerContext, svc moleculer.ServiceSchema) {
			mixinHook(ctx, svc)
			if svcHook != nil {
				svcHook(ctx, svc)
			}
		}
	}
	return service
}
func applyMixins(service moleculer.ServiceSchema) moleculer.ServiceSchema {
	for _, mixin := range service.Mixins {
		service = extendActions(service, &mixin)
		service = mergeDependencies(service, &mixin)
		service = concatenateEvents(service, &mixin)
		service = extendSettings(service, &mixin)
		service = extendMetadata(service, &mixin)
		service = extendHooks(service, &mixin)
		service = chainCreated(service, &mixin)
		service = chainStarted(service, &mixin)
		service = chainStopped(service, &mixin)
	}
	return service
}
func JoinVersionToName(name string, version string) string {
	if version != "" {
		return fmt.Sprintf("%s.%s", version, name)
	}
	return name
}
func CreateServiceEvent(eventName, serviceName, group string, handler moleculer.EventHandler) Event {
	return Event{
		eventName,
		serviceName,
		group,
		handler,
	}
}
func CreateServiceAction(serviceName string, actionName string, handler moleculer.ActionHandler, params moleculer.ActionSchema) Action {
	return Action{
		actionName,
		fmt.Sprintf("%s.%s", serviceName, actionName),
		handler,
		params,
	}
}
func (service *Service) AsMap() map[string]interface{} {
	serviceInfo := make(map[string]interface{})
	serviceInfo["name"] = service.name
	serviceInfo["version"] = service.version
	serviceInfo["settings"] = service.settings
	serviceInfo["metadata"] = service.metadata
	serviceInfo["nodeID"] = service.nodeID
	if service.nodeID == "" {
		panic("no service.nodeID")
	}
	actions := map[string]map[string]interface{}{}
	for _, serviceAction := range service.actions {
		if !isInternalAction(serviceAction) {
			actionInfo := make(map[string]interface{})
			actionInfo["name"] = serviceAction.fullname
			actionInfo["rawName"] = serviceAction.name
			actionInfo["params"] = paramsAsMap(&serviceAction.params)
			actions[serviceAction.fullname] = actionInfo
		}
	}
	serviceInfo["actions"] = actions
	events := map[string]map[string]interface{}{}
	for _, serviceEvent := range service.events {
		if !isInternalEvent(serviceEvent) {
			eventInfo := make(map[string]interface{})
			eventInfo["name"] = serviceEvent.name
			eventInfo["group"] = serviceEvent.group
			events[serviceEvent.name] = eventInfo
		}
	}
	serviceInfo["events"] = events
	return serviceInfo
}
func isInternalAction(action Action) bool {
	return strings.Index(action.Name(), "$") == 0
}
func isInternalEvent(event Event) bool {
	return strings.Index(event.Name(), "$") == 0
}
func paramsFromMap(schema interface{}) moleculer.ActionSchema {
	return moleculer.ObjectSchema{nil}
}
func paramsAsMap(params *moleculer.ActionSchema) map[string]interface{} {
	schema := make(map[string]interface{})
	return schema
}
func (service *Service) AddActionMap(actionInfo map[string]interface{}) *Action {
	action := CreateServiceAction(
		service.fullname,
		actionInfo["rawName"].(string),
		nil,
		paramsFromMap(actionInfo["schema"]),
	)
	service.actions = append(service.actions, action)
	return &action
}
func (service *Service) RemoveEvent(name string) {
	var newEvents []Event
	for _, event := range service.events {
		if event.name != name {
			newEvents = append(newEvents, event)
		}
	}
	service.events = newEvents
}
func (service *Service) RemoveAction(fullname string) {
	var newActions []Action
	for _, action := range service.actions {
		if action.fullname != fullname {
			newActions = append(newActions, action)
		}
	}
	service.actions = newActions
}
func (service *Service) AddEventMap(eventInfo map[string]interface{}) *Event {
	group, exists := eventInfo["group"]
	if !exists {
		group = service.name
	}
	serviceEvent := Event{
		name:        eventInfo["name"].(string),
		serviceName: service.name,
		group:       group.(string),
	}
	service.events = append(service.events, serviceEvent)
	return &serviceEvent
}
func (service *Service) UpdateFromMap(serviceInfo map[string]interface{}) {
	service.settings = serviceInfo["settings"].(map[string]interface{})
	service.metadata = serviceInfo["metadata"].(map[string]interface{})
}
func (service *Service) AddSettings(settings map[string]interface{}) {
	service.settings = MergeSettings(service.settings, settings)
}
func (service *Service) AddMetadata(metadata map[string]interface{}) {
	service.metadata = MergeSettings(service.metadata, metadata)
}
func populateFromMap(service *Service, serviceInfo map[string]interface{}) {
	if nodeID, ok := serviceInfo["nodeID"]; ok {
		service.nodeID = nodeID.(string)
	}
	service.version = ParseVersion(serviceInfo["version"])
	service.name = serviceInfo["name"].(string)
	service.fullname = JoinVersionToName(
		service.name,
		service.version)
	service.settings = serviceInfo["settings"].(map[string]interface{})
	service.metadata = serviceInfo["metadata"].(map[string]interface{})
	actions := serviceInfo["actions"].(map[string]interface{})
	for _, item := range actions {
		actionInfo := item.(map[string]interface{})
		service.AddActionMap(actionInfo)
	}
	events := serviceInfo["events"].(map[string]interface{})
	for _, item := range events {
		eventInfo := item.(map[string]interface{})
		service.AddEventMap(eventInfo)
	}
}
func (service *Service) populateFromSchema() {
	schema := service.schema
	service.name = schema.Name
	service.version = schema.Version
	service.fullname = JoinVersionToName(service.name, service.version)
	service.dependencies = schema.Dependencies
	service.settings = schema.Settings
	if service.settings == nil {
		service.settings = make(map[string]interface{})
	}
	service.metadata = schema.Metadata
	if service.metadata == nil {
		service.metadata = make(map[string]interface{})
	}
	service.actions = make([]Action, len(schema.Actions))
	for index, actionSchema := range schema.Actions {
		service.actions[index] = CreateServiceAction(
			service.fullname,
			actionSchema.Name,
			actionSchema.Handler,
			actionSchema.Schema,
		)
	}
	service.events = make([]Event, len(schema.Events))
	for index, eventSchema := range schema.Events {
		group := eventSchema.Group
		if group == "" {
			group = service.Name()
		}
		service.events[index] = Event{
			name:        eventSchema.Name,
			serviceName: service.Name(),
			group:       group,
			handler:     eventSchema.Handler,
		}
	}
	service.created = schema.Created
	service.started = schema.Started
	service.stopped = schema.Stopped
}
func copyVersion(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
	versioner, hasIt := obj.(HasVersion)
	if hasIt {
		schema.Version = versioner.Version()
	}
	return schema
}
func copyDependencies(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
	del, hasIt := obj.(HasDependencies)
	if hasIt {
		schema.Dependencies = del.Dependencies()
	}
	return schema
}
func copyEvents(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
	del, hasIt := obj.(HasEvents)
	if hasIt {
		schema.Events = del.Events()
	}
	return schema
}
func copyMetadata(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
	del, hasIt := obj.(HasMetadata)
	if hasIt {
		schema.Metadata = del.Metadata()
	}
	return schema
}
func copyMixins(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
	del, hasIt := obj.(HasMixins)
	if hasIt {
		schema.Mixins = del.Mixins()
	}
	return schema
}
func copySettings(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
	del, hasIt := obj.(HasSettings)
	if hasIt {
		schema.Settings = del.Settings()
	}
	return schema
}
var invalid = []string{
	"Name", "Version", "Dependencies", "Settings",
	"Metadata", "Mixins", "Events", "Created", "Started", "Stopped",
}
func validActionName(name string) bool {
	for _, item := range invalid {
		if item == name {
			return false
		}
	}
	return true
}
func actionName(name string) string {
	if len(name) < 2 {
		return strings.ToLower(name)
	}
	return strings.ToLower(name[:1]) + name[1:len(name)]
}
type aHandlerTemplate struct {
	match func(interface{}) bool
	wrap  func(reflect.Value, interface{}) moleculer.ActionHandler
}
func handlerTemplate(m reflect.Value) moleculer.ActionHandler {
	obj := m.Interface()
	for _, t := range actionHandlerTemplates {
		if t.match(obj) {
			return t.wrap(m, obj)
		}
	}
	return nil
}
func getParamTypes(m reflect.Value) []string {
	t := m.Type()
	result := make([]string, t.NumIn())
	for i := 0; i < t.NumIn(); i++ {
		result[i] = t.In(i).Name()
	}
	return result
}
func payloadToValue(t string, p moleculer.Payload) reflect.Value {
	if t == "Payload" {
		return reflect.ValueOf(p)
	}
	return reflect.ValueOf(p.Value())
}
func buildArgs(ptypes []string, p moleculer.Payload) []reflect.Value {
	args := []reflect.Value{}
	if p.IsArray() {
		list := p.Array()
		for i, t := range ptypes {
			v := payloadToValue(t, list[i])
			args = append(args, v)
		}
	} else if p.Exists() {
		v := payloadToValue(ptypes[0], p)
		args = append(args, v)
	}
	return args
}
func validateArgs(ptypes []string, p moleculer.Payload) error {
	if !p.IsArray() && len(ptypes) > 1 {
		return errors.New(fmt.Sprint("This action requires arguments to be sent in an array. #", len(ptypes), " arguments - types: ", ptypes))
	}
	if p.Len() != len(ptypes) && len(ptypes) > 1 {
		return errors.New(fmt.Sprint("This action requires #", len(ptypes), " arguments - types: ", ptypes))
	}
	return nil
}
func isError(v interface{}) bool {
	_, is := v.(error)
	return is
}
func checkReturn(in []reflect.Value) interface{} {
	if in == nil || len(in) == 0 {
		return nil
	}
	if len(in) == 1 {
		return in[0].Interface()
	}
	if isError(in[len(in)-1].Interface()) {
		return in[len(in)-1].Interface()
	}
	return valuesToPayload(in)
}
func valuesToPayload(vs []reflect.Value) moleculer.Payload {
	list := make([]interface{}, len(vs))
	for i, v := range vs {
		list[i] = v.Interface()
	}
	return payload.New(list)
}