github.com/ronaksoft/rony@v0.16.26-0.20230807065236-1743dbfe6959/internal/codegen/arg.go (about)

     1  package codegen
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/ronaksoft/rony"
     8  	"github.com/ronaksoft/rony/tools"
     9  	"google.golang.org/protobuf/compiler/protogen"
    10  	"google.golang.org/protobuf/proto"
    11  	"google.golang.org/protobuf/reflect/protoreflect"
    12  	"google.golang.org/protobuf/types/descriptorpb"
    13  )
    14  
    15  /*
    16     Creation Time: 2021 - Jul - 07
    17     Created by:  (ehsan)
    18     Maintainers:
    19        1.  Ehsan N. Moosa (E2)
    20     Auditor: Ehsan N. Moosa (E2)
    21     Copyright Ronak Software Group 2020
    22  */
    23  
    24  type TextCase string
    25  
    26  const (
    27  	None           TextCase = ""
    28  	CamelCase      TextCase = "CC"
    29  	LowerCamelCase TextCase = "LCC"
    30  	KebabCase      TextCase = "KC"
    31  	SnakeCase      TextCase = "SC"
    32  )
    33  
    34  type Language string
    35  
    36  const (
    37  	LangGo    Language = "GO"
    38  	LangCQL   Language = "CQL"
    39  	LangProto Language = "PROTO"
    40  )
    41  
    42  type TemplateArg struct {
    43  	Messages []MessageArg
    44  	Services []ServiceArg
    45  }
    46  
    47  func GenTemplateArg(f *protogen.File) *TemplateArg {
    48  	arg := &TemplateArg{}
    49  	for _, m := range f.Messages {
    50  		arg.Messages = append(arg.Messages, GetMessageArg(m).With(f))
    51  	}
    52  	for _, s := range f.Services {
    53  		arg.Services = append(arg.Services, getServiceArg(s).With(f))
    54  	}
    55  
    56  	return arg
    57  }
    58  
    59  // MessageArg holds the data needed by the template engine to generate code based on the protogen.Message
    60  type MessageArg struct {
    61  	file       *protogen.File
    62  	desc       *protogen.Message
    63  	name       string
    64  	pkg        string
    65  	Fields     []FieldArg
    66  	C          uint64
    67  	ImportPath protogen.GoImportPath
    68  	Comments   string
    69  
    70  	// If message is representing a model then following parameters are filled
    71  	IsAggregate bool
    72  	IsSingleton bool
    73  	GlobalRepo  string
    74  	LocalRepo   string
    75  	Table       *ModelKey
    76  	TableExtra  []Prop
    77  	Views       []*ModelKey
    78  }
    79  
    80  func GetMessageArg(m *protogen.Message) MessageArg {
    81  	arg := MessageArg{
    82  		desc: m,
    83  	}
    84  	arg.name = string(m.Desc.Name())
    85  	arg.pkg = string(m.Desc.ParentFile().Package())
    86  	arg.C = CrcHash([]byte(m.Desc.Name()))
    87  	arg.ImportPath = m.GoIdent.GoImportPath
    88  	for _, f := range m.Fields {
    89  		arg.Fields = append(arg.Fields, getFieldArg(f))
    90  	}
    91  	for _, c := range m.Comments.LeadingDetached {
    92  		arg.Comments += c.String() + "\r\n"
    93  	}
    94  
    95  	// Generate the aggregate description from proto options
    96  	opt, _ := m.Desc.Options().(*descriptorpb.MessageOptions)
    97  	modelOpt := proto.GetExtension(opt, rony.E_RonyModel).(*rony.ModelOpt)
    98  	if modelOpt == nil {
    99  		return arg
   100  	}
   101  
   102  	arg.GlobalRepo = strings.ToLower(modelOpt.GetGlobalDatasource())
   103  	arg.LocalRepo = strings.ToLower(modelOpt.GetLocalDatasource())
   104  	arg.IsSingleton = modelOpt.GetSingleton()
   105  
   106  	if arg.IsSingleton {
   107  		// if message is going to be singleton then it could not be aggregate
   108  		return arg
   109  	}
   110  
   111  	// If there is no table defined then it is not an aggregate
   112  	if modelOpt.Table == nil {
   113  		return arg
   114  	}
   115  
   116  	arg.IsAggregate = true
   117  	arg.parsePrimaryKeyOpt(m)
   118  
   119  	return arg
   120  }
   121  
   122  func (ma *MessageArg) parsePrimaryKeyOpt(m *protogen.Message) {
   123  	opt, _ := m.Desc.Options().(*descriptorpb.MessageOptions)
   124  	modelOpt := proto.GetExtension(opt, rony.E_RonyModel).(*rony.ModelOpt)
   125  	tablePK := modelOpt.Table
   126  	viewPKs := modelOpt.View
   127  
   128  	// Generate Go and CQL kinds of the fields
   129  	cqlTypes := map[string]string{}
   130  	goTypes := map[string]string{}
   131  	protoTypes := map[string]string{}
   132  	uniqueView := map[string]*ModelKey{}
   133  	extraProp := map[string]Prop{}
   134  	for _, f := range m.Fields {
   135  		protoTypes[f.GoName] = f.Desc.Kind().String()
   136  		cqlTypes[f.GoName] = CqlKind(f.Desc)
   137  		goTypes[f.GoName] = GoKind(f.Desc)
   138  	}
   139  
   140  	// Fill Table's ModelKey
   141  	ma.Table = &ModelKey{
   142  		Arg:   ma,
   143  		alias: tablePK.GetAlias(),
   144  	}
   145  	for _, k := range tablePK.PartKey {
   146  		ma.Table.pks = append(ma.Table.pks, Prop{
   147  			Name:      k,
   148  			ProtoType: protoTypes[k],
   149  			CqlType:   cqlTypes[k],
   150  			GoType:    goTypes[k],
   151  			Order:     "",
   152  		})
   153  	}
   154  	for _, k := range tablePK.SortKey {
   155  		order := ASC
   156  		if strings.HasPrefix(k, "-") {
   157  			order = DESC
   158  		}
   159  
   160  		k = strings.TrimLeft(k, "-")
   161  		ma.Table.cks = append(ma.Table.cks, Prop{
   162  			Name:      k,
   163  			ProtoType: protoTypes[k],
   164  			CqlType:   cqlTypes[k],
   165  			GoType:    goTypes[k],
   166  			Order:     order,
   167  		})
   168  	}
   169  
   170  	// Fill Views' ModelKey
   171  	for _, v := range viewPKs {
   172  		view := &ModelKey{
   173  			Arg:   ma,
   174  			alias: v.GetAlias(),
   175  		}
   176  		for _, k := range v.PartKey {
   177  			p := Prop{
   178  				Name:      k,
   179  				ProtoType: protoTypes[k],
   180  				CqlType:   cqlTypes[k],
   181  				GoType:    goTypes[k],
   182  				Order:     "",
   183  			}
   184  			if !ma.Table.HasProp(k) {
   185  				extraProp[k] = p
   186  			}
   187  			view.pks = append(view.pks, p)
   188  		}
   189  		for _, k := range v.SortKey {
   190  			order := ASC
   191  			if strings.HasPrefix(k, "-") {
   192  				order = DESC
   193  			}
   194  			k = strings.TrimLeft(k, "-")
   195  			p := Prop{
   196  				Name:      k,
   197  				ProtoType: protoTypes[k],
   198  				CqlType:   cqlTypes[k],
   199  				GoType:    goTypes[k],
   200  				Order:     order,
   201  			}
   202  			if !ma.Table.HasProp(k) {
   203  				extraProp[k] = p
   204  			}
   205  			view.cks = append(view.cks, p)
   206  		}
   207  		if oldView, ok := uniqueView[view.Names(PropFilterPKs, "", "", "", None)]; ok {
   208  			view.index = oldView.index + 1
   209  		}
   210  		uniqueView[view.Names(PropFilterPKs, "", "", "", None)] = view
   211  		ma.Views = append(ma.Views, view)
   212  	}
   213  	for _, p := range extraProp {
   214  		ma.TableExtra = append(ma.TableExtra, p)
   215  	}
   216  }
   217  
   218  func (ma *MessageArg) currentPkg() string {
   219  	var pkg string
   220  	if ma.file != nil {
   221  		pkg = string(ma.file.GoPackageName)
   222  	}
   223  
   224  	return pkg
   225  }
   226  
   227  func (ma MessageArg) Name() string {
   228  	return ma.name
   229  }
   230  
   231  func (ma MessageArg) NameCC() string {
   232  	return tools.ToLowerCamel(ma.name)
   233  }
   234  
   235  func (ma MessageArg) NameKC() string {
   236  	return tools.ToKebab(ma.name)
   237  }
   238  
   239  func (ma MessageArg) NameSC() string {
   240  	return tools.ToSnake(ma.name)
   241  }
   242  
   243  func (ma MessageArg) Pkg() string {
   244  	if ma.currentPkg() == ma.pkg {
   245  		return ""
   246  	}
   247  
   248  	return ma.pkg
   249  }
   250  
   251  func (ma MessageArg) ViewsByPK() map[string][]*ModelKey {
   252  	var res = map[string][]*ModelKey{}
   253  	for _, v := range ma.Views {
   254  		k := v.Names(PropFilterPKs, "", "", "", None)
   255  		res[k] = append(res[k], v)
   256  	}
   257  
   258  	return res
   259  }
   260  
   261  func (ma MessageArg) Fullname() string {
   262  	if ma.pkg == ma.currentPkg() {
   263  		return ma.name
   264  	} else {
   265  		return fmt.Sprintf("%s.%s", ma.pkg, ma.name)
   266  	}
   267  }
   268  
   269  func (ma MessageArg) CName() string {
   270  	return fmt.Sprintf("C_%s", ma.name)
   271  }
   272  
   273  func (ma MessageArg) With(f *protogen.File) MessageArg {
   274  	ma.file = f
   275  	for idx := range ma.Fields {
   276  		ma.Fields[idx] = ma.Fields[idx].With(f)
   277  	}
   278  
   279  	return ma
   280  }
   281  
   282  func (ma MessageArg) IsEnvelope() bool {
   283  	opt := ma.Options()
   284  	if opt == nil {
   285  		return false
   286  	}
   287  
   288  	return proto.GetExtension(opt, rony.E_RonyEnvelope).(bool)
   289  }
   290  
   291  func (ma MessageArg) SkipJson() bool {
   292  	opt := ma.Options()
   293  	if opt == nil {
   294  		return false
   295  	}
   296  
   297  	return proto.GetExtension(opt, rony.E_RonySkipJson).(bool)
   298  }
   299  
   300  func (ma MessageArg) Options() *descriptorpb.MessageOptions {
   301  	opt, _ := ma.desc.Desc.Options().(*descriptorpb.MessageOptions)
   302  
   303  	return opt
   304  }
   305  
   306  // FieldArg holds the data needed by the template engine to generate code based on the protogen.Field
   307  type FieldArg struct {
   308  	file             *protogen.File
   309  	desc             protoreflect.FieldDescriptor
   310  	name             string
   311  	pkg              string
   312  	ImportPath       protogen.GoImportPath
   313  	ZeroValue        string
   314  	Kind             string
   315  	ProtoKind        protoreflect.Kind
   316  	ProtoCardinality protoreflect.Cardinality
   317  	GoKind           string
   318  	CqlKind          string
   319  	Cardinality      string
   320  	HasIndex         bool
   321  	HelpText         string
   322  	DefaultValue     string
   323  }
   324  
   325  func getFieldArg(f *protogen.Field) FieldArg {
   326  	arg := FieldArg{
   327  		desc: f.Desc,
   328  	}
   329  
   330  	arg.name = f.GoName
   331  	if f.Message != nil {
   332  		arg.pkg = string(f.Message.Desc.ParentFile().Package())
   333  		arg.ImportPath = f.Message.GoIdent.GoImportPath
   334  	} else {
   335  		arg.pkg = string(f.Desc.ParentFile().Package())
   336  	}
   337  
   338  	arg.Kind = f.Desc.Kind().String()
   339  	arg.ProtoKind = f.Desc.Kind()
   340  	arg.ProtoCardinality = f.Desc.Cardinality()
   341  	arg.GoKind = GoKind(f.Desc)
   342  	arg.CqlKind = CqlKind(f.Desc)
   343  	arg.Cardinality = f.Desc.Cardinality().String()
   344  	arg.ZeroValue = ZeroValue(f.Desc)
   345  
   346  	opt, _ := f.Desc.Options().(*descriptorpb.FieldOptions)
   347  	arg.HasIndex = proto.GetExtension(opt, rony.E_RonyIndex).(bool)
   348  	arg.HelpText = proto.GetExtension(opt, rony.E_RonyHelp).(string)
   349  	arg.DefaultValue = proto.GetExtension(opt, rony.E_RonyDefault).(string)
   350  
   351  	return arg
   352  }
   353  
   354  func (fa FieldArg) currentPkg() string {
   355  	var pkg string
   356  	if fa.file != nil {
   357  		pkg = string(fa.file.GoPackageName)
   358  	}
   359  
   360  	return pkg
   361  }
   362  
   363  func (fa FieldArg) Name() string {
   364  	return fa.name
   365  }
   366  
   367  func (fa FieldArg) NameCC() string {
   368  	return tools.ToLowerCamel(fa.name)
   369  }
   370  
   371  func (fa FieldArg) NameKC() string {
   372  	return tools.ToKebab(fa.name)
   373  }
   374  
   375  func (fa FieldArg) NameSC() string {
   376  	return tools.ToSnake(fa.name)
   377  }
   378  
   379  func (fa FieldArg) JSONName() string {
   380  	return fa.desc.JSONName()
   381  }
   382  
   383  func (fa FieldArg) DescName() string {
   384  	return string(fa.desc.Name())
   385  }
   386  
   387  func (fa FieldArg) Type() string {
   388  	if fa.desc.Message() != nil {
   389  		return string(fa.desc.Message().Name())
   390  	}
   391  
   392  	return fa.GoKind
   393  }
   394  
   395  func (fa FieldArg) Pkg() string {
   396  	if fa.currentPkg() == fa.pkg {
   397  		return ""
   398  	}
   399  
   400  	return fa.pkg
   401  }
   402  
   403  func (fa FieldArg) With(f *protogen.File) FieldArg {
   404  	fa.file = f
   405  
   406  	return fa
   407  }
   408  
   409  func (fa FieldArg) Options() *descriptorpb.FieldOptions {
   410  	opt, _ := fa.desc.Options().(*descriptorpb.FieldOptions)
   411  
   412  	return opt
   413  }
   414  
   415  // ServiceArg holds the data needed by the template engine to generate code based on the protogen.Service
   416  type ServiceArg struct {
   417  	file         *protogen.File
   418  	desc         protoreflect.ServiceDescriptor
   419  	name         string
   420  	C            uint64
   421  	Methods      []MethodArg
   422  	Comments     string
   423  	HasRestProxy bool
   424  }
   425  
   426  func (sa ServiceArg) currentPkg() string {
   427  	var pkg string
   428  	if sa.file != nil {
   429  		pkg = string(sa.file.GoPackageName)
   430  	}
   431  
   432  	return pkg
   433  }
   434  
   435  func (sa ServiceArg) Name() string {
   436  	return sa.name
   437  }
   438  
   439  func (sa ServiceArg) NameCC() string {
   440  	return tools.ToLowerCamel(sa.name)
   441  }
   442  
   443  func (sa ServiceArg) NameKC() string {
   444  	return tools.ToKebab(sa.name)
   445  }
   446  
   447  func (sa ServiceArg) NameSC() string {
   448  	return tools.ToSnake(sa.name)
   449  }
   450  
   451  func (sa ServiceArg) With(f *protogen.File) ServiceArg {
   452  	sa.file = f
   453  	for idx := range sa.Methods {
   454  		sa.Methods[idx] = sa.Methods[idx].With(sa.file)
   455  	}
   456  
   457  	return sa
   458  }
   459  
   460  func (sa ServiceArg) Options() *descriptorpb.ServiceOptions {
   461  	opt, _ := sa.desc.Options().(*descriptorpb.ServiceOptions)
   462  
   463  	return opt
   464  }
   465  
   466  func getServiceArg(s *protogen.Service) ServiceArg {
   467  	arg := ServiceArg{
   468  		desc: s.Desc,
   469  	}
   470  	arg.name = string(s.Desc.Name())
   471  	arg.C = CrcHash([]byte(arg.name))
   472  	for _, c := range s.Comments.LeadingDetached {
   473  		arg.Comments += c.String() + "\r\n"
   474  	}
   475  
   476  	for _, m := range s.Methods {
   477  		ma := getMethodArg(s, m)
   478  		if ma.RestEnabled {
   479  			arg.HasRestProxy = true
   480  		}
   481  		if arg.currentPkg() != "" && arg.currentPkg() != ma.Input.pkg {
   482  			panic("input must be with in the same package of its service")
   483  		}
   484  		arg.Methods = append(arg.Methods, ma)
   485  	}
   486  
   487  	return arg
   488  }
   489  
   490  // MethodArg holds the data needed by the template engine to generate code based on the protogen.Method
   491  type MethodArg struct {
   492  	desc        protoreflect.MethodDescriptor
   493  	file        *protogen.File
   494  	name        string
   495  	fullname    string
   496  	C           uint64
   497  	Comments    string
   498  	Input       MessageArg
   499  	Output      MessageArg
   500  	RestEnabled bool
   501  	TunnelOnly  bool
   502  	Rest        RestArg
   503  }
   504  
   505  type RestArg struct {
   506  	Method      string
   507  	Path        string
   508  	Json        bool
   509  	Unmarshal   bool
   510  	ExtraCode   []string
   511  	PathParams  map[string]protoreflect.Kind
   512  	QueryParams map[string]protoreflect.Kind
   513  }
   514  
   515  func getRestArg(m *protogen.Method, arg *MethodArg) {
   516  	opt, _ := m.Desc.Options().(*descriptorpb.MethodOptions)
   517  	restOpt := proto.GetExtension(opt, rony.E_RonyRest).(*rony.RestOpt)
   518  	if restOpt == nil {
   519  		return
   520  	}
   521  
   522  	arg.RestEnabled = true
   523  	arg.Rest.Method = restOpt.GetMethod()
   524  	arg.Rest.Path = fmt.Sprintf("/%s", strings.Trim(restOpt.GetPath(), "/"))
   525  	arg.Rest.Json = restOpt.GetJsonEncode()
   526  
   527  	var (
   528  		pathParams  = make([]string, 0, 8)
   529  		queryParams = make([]string, 0, 8)
   530  	)
   531  	bindParams := map[string]string{}
   532  	for _, pv := range strings.Split(arg.Rest.Path, "/") {
   533  		if !strings.HasPrefix(pv, ":") {
   534  			continue
   535  		}
   536  		pathParam := strings.TrimLeft(pv, ":")
   537  		pathParams = append(pathParams, pathParam)
   538  		bindParams[pathParam] = pathParam
   539  	}
   540  	for _, bv := range restOpt.GetBindPathParam() {
   541  		parts := strings.SplitN(strings.TrimSpace(bv), "=", 2)
   542  		if len(parts) == 2 {
   543  			bindParams[parts[0]] = parts[1]
   544  		}
   545  	}
   546  	for _, bv := range restOpt.GetBindQueryParam() {
   547  		parts := strings.SplitN(strings.TrimSpace(bv), "=", 2)
   548  		if len(parts) == 2 {
   549  			bindParams[parts[0]] = parts[1]
   550  			queryParams = append(queryParams, strings.TrimSpace(parts[0]))
   551  		}
   552  	}
   553  
   554  	if len(arg.Input.Fields) > len(bindParams) {
   555  		arg.Rest.Unmarshal = true
   556  	}
   557  
   558  	for _, paramName := range pathParams {
   559  		fieldName := bindParams[paramName]
   560  		for _, f := range m.Input.Fields {
   561  			if ec := getExtraCode(f, fieldName, paramName); ec != "" {
   562  				arg.Rest.ExtraCode = append(arg.Rest.ExtraCode, ec)
   563  				arg.Rest.PathParams[paramName] = f.Desc.Kind()
   564  			}
   565  		}
   566  	}
   567  
   568  	for _, paramName := range queryParams {
   569  		fieldName := bindParams[paramName]
   570  		for _, f := range m.Input.Fields {
   571  			if ec := getExtraCode(f, fieldName, paramName); ec != "" {
   572  				arg.Rest.ExtraCode = append(arg.Rest.ExtraCode, ec)
   573  				arg.Rest.QueryParams[paramName] = f.Desc.Kind()
   574  			}
   575  		}
   576  	}
   577  
   578  	return
   579  }
   580  
   581  func getExtraCode(f *protogen.Field, fieldName, paramName string) (ec string) {
   582  	if string(f.Desc.Name()) != fieldName {
   583  		return
   584  	}
   585  
   586  	switch f.Desc.Kind() {
   587  	case protoreflect.Int64Kind, protoreflect.Sfixed64Kind, protoreflect.Sint64Kind:
   588  		ec = fmt.Sprint(
   589  			"req.",
   590  			f.GoName, "= tools.StrToInt64(tools.GetString(conn.Get(\"", paramName, "\"), \"0\"))",
   591  		)
   592  	case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
   593  		ec = fmt.Sprint(
   594  			"req.",
   595  			f.GoName, "= tools.StrToUInt64(tools.GetString(conn.Get(\"", paramName, "\"), \"0\"))",
   596  		)
   597  	case protoreflect.Int32Kind, protoreflect.Sfixed32Kind, protoreflect.Sint32Kind:
   598  		ec = fmt.Sprint(
   599  			"req.",
   600  			f.GoName, "= tools.StrToInt32(tools.GetString(conn.Get(\"", paramName, "\"), \"0\"))",
   601  		)
   602  	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
   603  		ec = fmt.Sprint(
   604  			"req.",
   605  			f.GoName, "= tools.StrToUInt32(tools.GetString(conn.Get(\"", paramName, "\"), \"0\"))",
   606  		)
   607  	case protoreflect.StringKind:
   608  		ec = fmt.Sprint(
   609  			"req.",
   610  			f.GoName, "= tools.GetString(conn.Get(\"", paramName, "\"), \"\")",
   611  		)
   612  	case protoreflect.BytesKind:
   613  		ec = fmt.Sprint(
   614  			"req.",
   615  			f.GoName, "= tools.S2B(tools.GetString(conn.Get(\"", paramName, "\"), \"\"))",
   616  		)
   617  	case protoreflect.DoubleKind:
   618  		ec = fmt.Sprint(
   619  			"req.",
   620  			f.GoName, "= tools.StrToFloat32(tools.GetString(conn.Get(\"", paramName, "\"), \"0\"))",
   621  		)
   622  	default:
   623  		ec = ""
   624  	}
   625  
   626  	return
   627  }
   628  
   629  func (ma MethodArg) Fullname() string {
   630  	return ma.fullname
   631  }
   632  
   633  func (ma MethodArg) Name() string {
   634  	return ma.name
   635  }
   636  
   637  func (ma MethodArg) NameCC() string {
   638  	return tools.ToLowerCamel(ma.name)
   639  }
   640  
   641  func (ma MethodArg) NameKC() string {
   642  	return tools.ToKebab(ma.name)
   643  }
   644  
   645  func (ma MethodArg) NameSC() string {
   646  	return tools.ToSnake(ma.name)
   647  }
   648  
   649  func (ma MethodArg) With(f *protogen.File) MethodArg {
   650  	ma.file = f
   651  	ma.Input = ma.Input.With(f)
   652  	ma.Output = ma.Output.With(f)
   653  
   654  	return ma
   655  }
   656  
   657  func (ma MethodArg) Options() *descriptorpb.MethodOptions {
   658  	opt, _ := ma.desc.Options().(*descriptorpb.MethodOptions)
   659  
   660  	return opt
   661  }
   662  
   663  func getMethodArg(s *protogen.Service, m *protogen.Method) MethodArg {
   664  	arg := MethodArg{
   665  		desc: m.Desc,
   666  		Rest: RestArg{
   667  			Method:      "",
   668  			Path:        "",
   669  			Json:        false,
   670  			PathParams:  map[string]protoreflect.Kind{},
   671  			QueryParams: map[string]protoreflect.Kind{},
   672  		},
   673  	}
   674  	arg.fullname = fmt.Sprintf("%s%s", s.Desc.Name(), m.Desc.Name())
   675  	arg.name = string(m.Desc.Name())
   676  	arg.C = CrcHash([]byte(fmt.Sprintf("%s%s", s.Desc.Name(), m.Desc.Name())))
   677  	arg.Input = GetMessageArg(m.Input)
   678  	arg.Output = GetMessageArg(m.Output)
   679  	for _, c := range m.Comments.LeadingDetached {
   680  		arg.Comments += c.String() + "\r\n"
   681  	}
   682  
   683  	if arg.Input.IsSingleton || arg.Input.IsAggregate {
   684  		panic("method input cannot be aggregate or singleton")
   685  	}
   686  	if arg.Output.IsSingleton || arg.Output.IsAggregate {
   687  		panic("method output cannot be aggregate or singleton")
   688  	}
   689  
   690  	opt, _ := m.Desc.Options().(*descriptorpb.MethodOptions)
   691  	getRestArg(m, &arg)
   692  
   693  	arg.TunnelOnly = proto.GetExtension(opt, rony.E_RonyInternal).(bool)
   694  
   695  	return arg
   696  }