github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/cmd/generates/modules/code.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package modules
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"fmt"
    24  	"github.com/aacfactory/errors"
    25  	"github.com/aacfactory/fns/cmd/generates/sources"
    26  	"github.com/aacfactory/gcg"
    27  	"os"
    28  	"path/filepath"
    29  	"strings"
    30  )
    31  
    32  func NewServiceFile(service *Service, annotations FnAnnotationCodeWriters) (file CodeFileWriter) {
    33  	file = &ServiceFile{
    34  		service:     service,
    35  		annotations: annotations,
    36  	}
    37  	return
    38  }
    39  
    40  type ServiceFile struct {
    41  	service     *Service
    42  	annotations FnAnnotationCodeWriters
    43  }
    44  
    45  func (s *ServiceFile) Name() (name string) {
    46  	name = filepath.ToSlash(filepath.Join(s.service.Dir, "fns.go"))
    47  	return
    48  }
    49  
    50  func (s *ServiceFile) Write(ctx context.Context) (err error) {
    51  	if ctx.Err() != nil {
    52  		err = errors.Warning("modules: service write failed").
    53  			WithMeta("kind", "service").WithMeta("service", s.service.Name).
    54  			WithCause(ctx.Err())
    55  		return
    56  	}
    57  
    58  	file := gcg.NewFileWithoutNote(s.service.Path[strings.LastIndex(s.service.Path, "/")+1:])
    59  	// comments
    60  	file.FileComments("NOTE: this file has been automatically generated, DON'T EDIT IT!!!\n")
    61  
    62  	// imports
    63  	packages, importsErr := s.importsCode(ctx)
    64  	if importsErr != nil {
    65  		err = errors.Warning("modules: code file write failed").
    66  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
    67  			WithCause(importsErr)
    68  		return
    69  	}
    70  	if packages != nil && len(packages) > 0 {
    71  		for _, importer := range packages {
    72  			file.AddImport(importer)
    73  		}
    74  	}
    75  
    76  	// names
    77  	names, namesErr := s.constNamesCode(ctx)
    78  	if namesErr != nil {
    79  		err = errors.Warning("modules: code file write failed").
    80  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
    81  			WithCause(namesErr)
    82  		return
    83  	}
    84  	file.AddCode(names)
    85  
    86  	// fn handler and proxy
    87  	proxies, proxiesErr := s.functionProxiesCode(ctx)
    88  	if proxiesErr != nil {
    89  		err = errors.Warning("modules: code file write failed").
    90  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
    91  			WithCause(proxiesErr)
    92  		return
    93  	}
    94  	file.AddCode(proxies)
    95  
    96  	// componentCode
    97  	component, componentErr := s.componentCode(ctx)
    98  	if componentErr != nil {
    99  		err = errors.Warning("modules: code file write failed").
   100  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   101  			WithCause(componentErr)
   102  		return
   103  	}
   104  	file.AddCode(component)
   105  
   106  	// service
   107  	service, serviceErr := s.serviceCode(ctx)
   108  	if serviceErr != nil {
   109  		err = errors.Warning("modules: code file write failed").
   110  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   111  			WithCause(serviceErr)
   112  		return
   113  	}
   114  	file.AddCode(service)
   115  
   116  	buf := bytes.NewBuffer([]byte{})
   117  
   118  	renderErr := file.Render(buf)
   119  	if renderErr != nil {
   120  		err = errors.Warning("modules: code file write failed").
   121  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   122  			WithCause(renderErr)
   123  		return
   124  	}
   125  	writer, openErr := os.OpenFile(s.Name(), os.O_CREATE|os.O_TRUNC|os.O_RDWR|os.O_SYNC, 0644)
   126  	if openErr != nil {
   127  		err = errors.Warning("modules: code file write failed").
   128  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   129  			WithCause(openErr)
   130  		return
   131  	}
   132  	n := 0
   133  	bodyLen := buf.Len()
   134  	body := buf.Bytes()
   135  	for n < bodyLen {
   136  		nn, writeErr := writer.Write(body[n:])
   137  		if writeErr != nil {
   138  			err = errors.Warning("modules: code file write failed").
   139  				WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   140  				WithCause(writeErr)
   141  			return
   142  		}
   143  		n += nn
   144  	}
   145  	syncErr := writer.Sync()
   146  	if syncErr != nil {
   147  		err = errors.Warning("modules: code file write failed").
   148  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   149  			WithCause(syncErr)
   150  		return
   151  	}
   152  	closeErr := writer.Close()
   153  	if closeErr != nil {
   154  		err = errors.Warning("modules: code file write failed").
   155  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   156  			WithCause(closeErr)
   157  		return
   158  	}
   159  	return
   160  }
   161  
   162  func (s *ServiceFile) importsCode(ctx context.Context) (packages []*gcg.Package, err error) {
   163  	if ctx.Err() != nil {
   164  		err = errors.Warning("modules: service write failed").
   165  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   166  			WithCause(ctx.Err())
   167  		return
   168  	}
   169  	packages = make([]*gcg.Package, 0, 1)
   170  	for _, i := range s.service.Imports {
   171  		if i.Alias != "" {
   172  			packages = append(packages, gcg.NewPackageWithAlias(i.Path, i.Alias))
   173  		} else {
   174  			packages = append(packages, gcg.NewPackage(i.Path))
   175  		}
   176  	}
   177  	return
   178  }
   179  
   180  func (s *ServiceFile) constNamesCode(ctx context.Context) (code gcg.Code, err error) {
   181  	if ctx.Err() != nil {
   182  		err = errors.Warning("modules: service write failed").
   183  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   184  			WithCause(ctx.Err())
   185  		return
   186  	}
   187  	stmt := gcg.Vars()
   188  	stmt.Add(gcg.Var("_endpointName", gcg.Token(fmt.Sprintf(" = []byte(\"%s\")", s.service.Name))))
   189  	for _, function := range s.service.Functions {
   190  		stmt.Add(gcg.Var(function.VarIdent, gcg.Token(fmt.Sprintf(" = []byte(\"%s\")", function.Name()))))
   191  	}
   192  	code = stmt.Build()
   193  	return
   194  }
   195  
   196  func (s *ServiceFile) componentCode(ctx context.Context) (code gcg.Code, err error) {
   197  	if ctx.Err() != nil {
   198  		err = errors.Warning("modules: service write failed").
   199  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   200  			WithCause(ctx.Err())
   201  		return
   202  	}
   203  	stmt := gcg.Statements()
   204  	stmt.Add(gcg.Token("// +-------------------------------------------------------------------------------------------------------------------+").Line().Line())
   205  
   206  	fn := gcg.Func()
   207  	fn.Name("Component[C services.Component]")
   208  	fn.AddParam("ctx", contextCode())
   209  	fn.AddParam("name", gcg.Token("string"))
   210  	fn.AddResult("component", gcg.Token("C"))
   211  	fn.AddResult("has", gcg.Token("bool"))
   212  	body := gcg.Statements()
   213  	body.Tab().Token("component, has = services.LoadComponent[C](ctx, _endpointName, name)").Line()
   214  	body.Tab().Return()
   215  	fn.Body(body)
   216  
   217  	stmt.Add(fn.Build()).Line()
   218  	code = stmt
   219  	return
   220  }
   221  
   222  func (s *ServiceFile) serviceCode(ctx context.Context) (code gcg.Code, err error) {
   223  	if ctx.Err() != nil {
   224  		err = errors.Warning("modules: service write failed").
   225  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   226  			WithCause(ctx.Err())
   227  		return
   228  	}
   229  	stmt := gcg.Statements()
   230  	// instance
   231  	stmt.Add(gcg.Token("// +-------------------------------------------------------------------------------------------------------------------+").Line().Line())
   232  	instanceCode, instanceCodeErr := s.serviceInstanceCode(ctx)
   233  	if instanceCodeErr != nil {
   234  		err = instanceCodeErr
   235  		return
   236  	}
   237  	stmt.Add(instanceCode).Line()
   238  
   239  	// type
   240  	stmt.Add(gcg.Token("// +-------------------------------------------------------------------------------------------------------------------+").Line().Line())
   241  	typeCode, typeCodeErr := s.serviceTypeCode(ctx)
   242  	if typeCodeErr != nil {
   243  		err = typeCodeErr
   244  		return
   245  	}
   246  	stmt.Add(typeCode).Line()
   247  	// construct
   248  	constructFnCode, constructCodeErr := s.serviceConstructCode(ctx)
   249  	if constructCodeErr != nil {
   250  		err = constructCodeErr
   251  		return
   252  	}
   253  	if constructFnCode != nil {
   254  		stmt.Add(constructFnCode).Line()
   255  	}
   256  	// doc
   257  	docCode, docCodeErr := s.serviceDocumentCode(ctx)
   258  	if docCodeErr != nil {
   259  		err = docCodeErr
   260  		return
   261  	}
   262  	if docCode != nil {
   263  		stmt.Add(docCode).Line()
   264  	}
   265  
   266  	code = stmt
   267  	return
   268  }
   269  
   270  func (s *ServiceFile) serviceInstanceCode(ctx context.Context) (code gcg.Code, err error) {
   271  	if ctx.Err() != nil {
   272  		err = errors.Warning("modules: service write failed").
   273  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   274  			WithCause(ctx.Err())
   275  		return
   276  	}
   277  	instance := gcg.Func()
   278  	instance.Name("Service")
   279  	instance.AddResult("v", gcg.Token("services.Service"))
   280  	body := gcg.Statements()
   281  	body.Tab().Token("v = &_service{").Line()
   282  	body.Tab().Tab().Token("Abstract: services.NewAbstract(").Line()
   283  	body.Tab().Tab().Tab().Token("string(_endpointName),").Line()
   284  	if s.service.Internal {
   285  		body.Tab().Tab().Tab().Token("true,").Line()
   286  	} else {
   287  		body.Tab().Tab().Tab().Token("false,").Line()
   288  	}
   289  	if s.service.Components != nil && s.service.Components.Len() > 0 {
   290  		path := fmt.Sprintf("%s/components", s.service.Path)
   291  		for _, component := range s.service.Components {
   292  			componentCode := gcg.QualifiedIdent(gcg.NewPackage(path), component.Indent)
   293  			body.Tab().Tab().Token("&").Add(componentCode).Token("{}").Symbol(",").Line()
   294  		}
   295  	}
   296  	body.Tab().Tab().Symbol(")").Symbol(",").Line()
   297  	body.Tab().Symbol("}").Line()
   298  	body.Tab().Return()
   299  
   300  	instance.Body(body)
   301  	code = instance.Build()
   302  	return
   303  }
   304  
   305  func (s *ServiceFile) serviceTypeCode(ctx context.Context) (code gcg.Code, err error) {
   306  	if ctx.Err() != nil {
   307  		err = errors.Warning("modules: service write failed").
   308  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   309  			WithCause(ctx.Err())
   310  		return
   311  	}
   312  	abstractFieldCode := gcg.StructField("")
   313  	abstractFieldCode.Type(gcg.Token("services.Abstract", gcg.NewPackage("github.com/aacfactory/fns/services")))
   314  	serviceStructCode := gcg.Struct()
   315  	serviceStructCode.AddField(abstractFieldCode)
   316  	code = gcg.Type("_service", serviceStructCode.Build())
   317  	return
   318  }
   319  
   320  func (s *ServiceFile) serviceConstructCode(ctx context.Context) (code gcg.Code, err error) {
   321  	if ctx.Err() != nil {
   322  		err = errors.Warning("modules: service write failed").
   323  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   324  			WithCause(ctx.Err())
   325  		return
   326  	}
   327  	construct := gcg.Func()
   328  	construct.Name("Construct")
   329  	construct.Receiver("svc", gcg.Star().Ident("_service"))
   330  	construct.AddParam("options", gcg.QualifiedIdent(gcg.NewPackage("github.com/aacfactory/fns/services"), "Options"))
   331  	construct.AddResult("err", gcg.Ident("error"))
   332  
   333  	body := gcg.Statements()
   334  	body.Tab().Token("if err = svc.Abstract.Construct(options); err != nil {").Line()
   335  	body.Tab().Tab().Token("return").Line()
   336  	body.Tab().Token("}").Line()
   337  
   338  	for _, function := range s.service.Functions {
   339  		param := gcg.QualifiedIdent(gcg.NewPackage("github.com/aacfactory/fns/services"), "Empty")
   340  		if function.Param != nil {
   341  			if s.service.Path == function.Param.Type.Path {
   342  				param = gcg.Ident(function.Param.Type.Name)
   343  			} else {
   344  				pkg, hasPKG := s.service.Imports.Path(function.Param.Type.Path)
   345  				if !hasPKG {
   346  					err = errors.Warning("modules: make function handle function code failed").
   347  						WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   348  						WithMeta("function", function.Name()).
   349  						WithCause(errors.Warning("import of param was not found").WithMeta("path", function.Param.Type.Path))
   350  					return
   351  				}
   352  				if pkg.Alias == "" {
   353  					param = gcg.QualifiedIdent(gcg.NewPackage(pkg.Path), function.Param.Type.Name)
   354  				} else {
   355  					param = gcg.QualifiedIdent(gcg.NewPackageWithAlias(pkg.Path, pkg.Alias), function.Param.Type.Name)
   356  				}
   357  			}
   358  		}
   359  		result := gcg.QualifiedIdent(gcg.NewPackage("github.com/aacfactory/fns/services"), "Empty")
   360  		if function.Result != nil {
   361  			if s.service.Path == function.Result.Type.Path {
   362  				result = gcg.Ident(function.Result.Type.Name)
   363  			} else {
   364  				pkg, hasPKG := s.service.Imports.Path(function.Result.Type.Path)
   365  				if !hasPKG {
   366  					err = errors.Warning("modules: make function proxy code failed").
   367  						WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   368  						WithMeta("function", function.Name()).
   369  						WithCause(errors.Warning("import of result was not found").WithMeta("path", function.Result.Type.Path))
   370  					return
   371  				}
   372  				if pkg.Alias == "" {
   373  					result = gcg.QualifiedIdent(gcg.NewPackage(pkg.Path), function.Result.Type.Name)
   374  				} else {
   375  					result = gcg.QualifiedIdent(gcg.NewPackageWithAlias(pkg.Path, pkg.Alias), function.Result.Type.Name)
   376  				}
   377  			}
   378  		}
   379  		body.Tab().Token(fmt.Sprintf("// %s", function.Name())).Line()
   380  		body.Tab().Token("svc.AddFunction(")
   381  		body.Token("commons.NewFn[").Add(param).Token(", ").Add(result).Token("](").Line()
   382  		body.Token(fmt.Sprintf("string(%s),", function.VarIdent)).Line()
   383  		body.Token("func(ctx context.Context, param ").Add(param).Token(") (v ").Add(result).Token(", err error) {").Line()
   384  		// annotation writers before
   385  		matchedAnnotations := make([]sources.Annotation, 0, 1)
   386  		matchedAnnotationWriters := make([]FnAnnotationCodeWriter, 0, 1)
   387  		for _, annotation := range function.Annotations {
   388  			annotationWriter, hasAnnotationWriter := s.annotations.Get(annotation.Name)
   389  			if hasAnnotationWriter {
   390  				matchedAnnotations = append(matchedAnnotations, annotation)
   391  				matchedAnnotationWriters = append(matchedAnnotationWriters, annotationWriter)
   392  			}
   393  		}
   394  		for i, annotationWriter := range matchedAnnotationWriters {
   395  			annotationCode, annotationCodeErr := annotationWriter.HandleBefore(ctx, matchedAnnotations[i].Params, function.Param != nil, function.Result != nil)
   396  			if annotationCodeErr != nil {
   397  				err = errors.Warning("modules: make function proxy code failed").
   398  					WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   399  					WithMeta("function", function.Name()).
   400  					WithCause(annotationCodeErr).WithMeta("annotation", annotationWriter.Annotation())
   401  				return
   402  			}
   403  			if annotationCode != nil {
   404  				body.Tab().Token("// generated by " + annotationWriter.Annotation()).Line()
   405  				body.Add(annotationCode).Line()
   406  			}
   407  		}
   408  		// handle
   409  		body.Tab().Token("// handle").Line()
   410  		if function.Param == nil && function.Result == nil {
   411  			body.Tab().Token(fmt.Sprintf("err = %s(ctx)", function.Ident)).Line()
   412  		} else if function.Param == nil && function.Result != nil {
   413  			body.Tab().Token(fmt.Sprintf("v, err = %s(ctx)", function.Ident)).Line()
   414  		} else if function.Param != nil && function.Result == nil {
   415  			body.Tab().Token(fmt.Sprintf("err = %s(ctx, param)", function.Ident)).Line()
   416  		} else {
   417  			body.Tab().Token(fmt.Sprintf("v, err = %s(ctx, param)", function.Ident)).Line()
   418  		}
   419  		// todo
   420  		body.Tab().Token("if err != nil {").Line()
   421  		body.Tab().Tab().Token("return").Line()
   422  		body.Tab().Token("}").Line()
   423  		// annotation writers after
   424  		for i, annotationWriter := range matchedAnnotationWriters {
   425  			annotationCode, annotationCodeErr := annotationWriter.HandleAfter(ctx, matchedAnnotations[i].Params, function.Param != nil, function.Result != nil)
   426  			if annotationCodeErr != nil {
   427  				err = errors.Warning("modules: make function handler code failed").
   428  					WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   429  					WithMeta("function", function.Name()).
   430  					WithCause(annotationCodeErr).WithMeta("annotation", annotationWriter.Annotation())
   431  				return
   432  			}
   433  			if annotationCode != nil {
   434  				body.Tab().Token("// generated by " + annotationWriter.Annotation()).Line()
   435  				body.Add(annotationCode).Line()
   436  			}
   437  		}
   438  		body.Tab().Token("return")
   439  		body.Token("},").Line()
   440  		if function.Readonly() {
   441  			body.Token("commons.Readonly(),").Line()
   442  		}
   443  		if function.Internal() {
   444  			body.Token("commons.Internal(),").Line()
   445  		}
   446  		if function.Deprecated() {
   447  			body.Token("commons.Deprecated(),").Line()
   448  		}
   449  		if validation, hasValidation := function.Validation(); hasValidation {
   450  			body.Token(fmt.Sprintf("commons.Validation(\"%s\"),", validation)).Line()
   451  		}
   452  		if function.Authorization() {
   453  			body.Token("commons.Authorization(),").Line()
   454  		}
   455  		if function.Permission() {
   456  			body.Token("commons.Permission(),").Line()
   457  		}
   458  		if function.Barrier() {
   459  			body.Token("commons.Barrier(),").Line()
   460  		}
   461  		if function.Metric() {
   462  			body.Token("commons.Metric(),").Line()
   463  		}
   464  		if cmd, ttl, hasCache := function.Cache(); hasCache {
   465  			body.Token(fmt.Sprintf("commons.Cache(\"%s\", \"%s\"),", cmd, ttl)).Line()
   466  		}
   467  		maxAge, public, mustRevalidate, proxyRevalidate, hasCC, ccErr := function.CacheControl()
   468  		if ccErr != nil {
   469  			err = errors.Warning("modules: make function handler code failed").
   470  				WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   471  				WithMeta("function", function.Name()).
   472  				WithCause(ccErr).WithMeta("annotation", "@cache-control")
   473  			return
   474  		}
   475  		if hasCC {
   476  			body.Token(fmt.Sprintf("commons.CacheControl(%d, %v, %v, %v),", maxAge, public, mustRevalidate, proxyRevalidate)).Line()
   477  		}
   478  		body.Token("))").Line()
   479  	}
   480  	body.Tab().Return()
   481  	construct.Body(body)
   482  	code = construct.Build()
   483  	return
   484  }
   485  
   486  func (s *ServiceFile) serviceDocumentCode(ctx context.Context) (code gcg.Code, err error) {
   487  	if ctx.Err() != nil {
   488  		err = errors.Warning("modules: service write failed").
   489  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   490  			WithCause(ctx.Err())
   491  		return
   492  	}
   493  	if s.service.Title == "" {
   494  		s.service.Title = s.service.Name
   495  	}
   496  	fnCodes := make([]gcg.Code, 0, 1)
   497  	for _, function := range s.service.Functions {
   498  		if function.Internal() {
   499  			continue
   500  		}
   501  		fnCode := gcg.Statements()
   502  		fnCode.Token("// ").Token(function.Name()).Line()
   503  		fnCode.Token("document.AddFn(").Line()
   504  		fnCode.Tab().Token("documents.NewFn(").Token("\"").Token(function.Name()).Token("\"").Token(")").Dot().Line()
   505  		fnCode.Tab().Tab().Token("SetInfo(").Token("\"").Token(strings.ReplaceAll(function.Title(), "\n", "\\n")).Token("\", ").Token("\"").Token(strings.ReplaceAll(function.Description(), "\n", "\\n")).Token("\"").Token(")").Dot().Line()
   506  		fnCode.Tab().Tab().
   507  			Token(fmt.Sprintf("SetReadonly(%v)", function.Readonly())).Dot().
   508  			Token(fmt.Sprintf("SetInternal(%v)", function.Internal())).Dot().
   509  			Token(fmt.Sprintf("SetDeprecated(%v)", function.Deprecated())).Dot().Line()
   510  		fnCode.Tab().Tab().
   511  			Token(fmt.Sprintf("SetAuthorization(%v)", function.Authorization())).Dot().
   512  			Token(fmt.Sprintf("SetPermission(%v)", function.Permission())).Dot().Line()
   513  		if function.Param == nil {
   514  			fnCode.Tab().Tab().Token("SetParam(documents.Nil())").Dot().Line()
   515  		} else {
   516  			paramCode, paramCodeErr := mapTypeToFunctionElementCode(ctx, function.Param.Type)
   517  			if paramCodeErr != nil {
   518  				err = errors.Warning("modules: make service document code failed").
   519  					WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   520  					WithMeta("function", function.Name()).
   521  					WithCause(paramCodeErr)
   522  				return
   523  			}
   524  			fnCode.Tab().Tab().Token("SetParam(").Add(paramCode).Token(")").Dot().Line()
   525  		}
   526  		if function.Result == nil {
   527  			fnCode.Tab().Tab().Token("SetResult(documents.Nil())").Dot().Line()
   528  		} else {
   529  			resultCode, resultCodeErr := mapTypeToFunctionElementCode(ctx, function.Result.Type)
   530  			if resultCodeErr != nil {
   531  				err = errors.Warning("modules: make service document code failed").
   532  					WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   533  					WithMeta("function", function.Name()).
   534  					WithCause(resultCodeErr)
   535  				return
   536  			}
   537  			fnCode.Tab().Tab().Token("SetResult(").Add(resultCode).Token(")").Dot().Line()
   538  		}
   539  		fnCode.Tab().Token(fmt.Sprintf("SetErrors(\"%s\"),", strings.ReplaceAll(function.Errors(), "\n", "\\n"))).Line()
   540  		fnCode.Token(")")
   541  		fnCodes = append(fnCodes, fnCode)
   542  	}
   543  	if len(fnCodes) == 0 {
   544  		return
   545  	}
   546  	docFnCode := gcg.Func()
   547  	docFnCode.Receiver("svc", gcg.Star().Ident("_service"))
   548  	docFnCode.Name("Document")
   549  	docFnCode.AddResult("document", gcg.QualifiedIdent(gcg.NewPackage("github.com/aacfactory/fns/services/documents"), "Endpoint"))
   550  	body := gcg.Statements()
   551  	body.Token(fmt.Sprintf("document = documents.New(svc.Name(), \"%s\", \"%s\", svc.Version())", strings.ReplaceAll(s.service.Title, "\n", "\\n"), strings.ReplaceAll(s.service.Description, "\n", "\\n")))
   552  	if s.service.Internal {
   553  		body.Token("document.SetInternal()").Line()
   554  	}
   555  	for _, fnCode := range fnCodes {
   556  		body.Line().Add(fnCode).Line()
   557  	}
   558  	body.Tab().Return()
   559  	docFnCode.Body(body)
   560  	code = docFnCode.Build()
   561  	return
   562  }
   563  
   564  func (s *ServiceFile) functionProxiesCode(ctx context.Context) (code gcg.Code, err error) {
   565  	if ctx.Err() != nil {
   566  		err = errors.Warning("modules: service write failed").
   567  			WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   568  			WithCause(ctx.Err())
   569  		return
   570  	}
   571  	stmt := gcg.Statements()
   572  	for _, function := range s.service.Functions {
   573  		stmt.Add(gcg.Token("// +-------------------------------------------------------------------------------------------------------------------+").Line().Line())
   574  		// proxy
   575  		proxy, proxyErr := s.functionProxyCode(ctx, function)
   576  		if proxyErr != nil {
   577  			err = proxyErr
   578  			return
   579  		}
   580  		stmt.Add(proxy).Line()
   581  		// proxy async
   582  		proxyAsync, proxyAsyncErr := s.functionProxyAsyncCode(ctx, function)
   583  		if proxyAsyncErr != nil {
   584  			err = proxyAsyncErr
   585  			return
   586  		}
   587  		stmt.Add(proxyAsync).Line()
   588  	}
   589  	code = stmt
   590  	return
   591  }
   592  
   593  func (s *ServiceFile) functionProxyAsyncCode(ctx context.Context, function *Function) (code gcg.Code, err error) {
   594  	proxyIdent := function.ProxyAsyncIdent
   595  	proxy := gcg.Func()
   596  	proxy.Name(proxyIdent)
   597  	proxy.AddParam("ctx", contextCode())
   598  	if function.Param != nil {
   599  		var param gcg.Code = nil
   600  		if s.service.Path == function.Param.Type.Path {
   601  			param = gcg.Ident(function.Param.Type.Name)
   602  		} else {
   603  			pkg, hasPKG := s.service.Imports.Path(function.Param.Type.Path)
   604  			if !hasPKG {
   605  				err = errors.Warning("modules: make function proxy code failed").
   606  					WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   607  					WithMeta("function", function.Name()).
   608  					WithCause(errors.Warning("import of param was not found").WithMeta("path", function.Param.Type.Path))
   609  				return
   610  			}
   611  			if pkg.Alias == "" {
   612  				param = gcg.QualifiedIdent(gcg.NewPackage(pkg.Path), function.Param.Type.Name)
   613  			} else {
   614  				param = gcg.QualifiedIdent(gcg.NewPackageWithAlias(pkg.Path, pkg.Alias), function.Param.Type.Name)
   615  			}
   616  		}
   617  		proxy.AddParam("param", param)
   618  	}
   619  	var result gcg.Code = nil
   620  	if function.Result != nil {
   621  		if s.service.Path == function.Result.Type.Path {
   622  			result = gcg.Ident(function.Result.Type.Name)
   623  		} else {
   624  			pkg, hasPKG := s.service.Imports.Path(function.Result.Type.Path)
   625  			if !hasPKG {
   626  				err = errors.Warning("modules: make function proxy code failed").
   627  					WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   628  					WithMeta("function", function.Name()).
   629  					WithCause(errors.Warning("import of result was not found").WithMeta("path", function.Result.Type.Path))
   630  				return
   631  			}
   632  			if pkg.Alias == "" {
   633  				result = gcg.QualifiedIdent(gcg.NewPackage(pkg.Path), function.Result.Type.Name)
   634  			} else {
   635  				result = gcg.QualifiedIdent(gcg.NewPackageWithAlias(pkg.Path, pkg.Alias), function.Result.Type.Name)
   636  			}
   637  		}
   638  	}
   639  	proxy.AddResult("future", gcg.QualifiedIdent(gcg.NewPackage("github.com/aacfactory/fns/commons/futures"), "Future"))
   640  	proxy.AddResult("err", gcg.Ident("error"))
   641  	// body >>>
   642  	body := gcg.Statements()
   643  	if function.Param != nil {
   644  		// validate
   645  		if validTitle, valid := function.Validation(); valid {
   646  			body.Tab().Token("// validate param").Line()
   647  			if validTitle == "" {
   648  				body.Tab().Token("if err = validators.Validate(param); err != nil {", gcg.NewPackage("github.com/aacfactory/fns/services/validators")).Line()
   649  				body.Tab().Tab().Token("return").Line()
   650  				body.Tab().Token("}").Line()
   651  			} else {
   652  				body.Tab().Token(fmt.Sprintf("if err = validators.ValidateWithErrorTitle(param, \"%s\"); err != nil {", validTitle)).Line()
   653  				body.Tab().Tab().Token("return").Line()
   654  				body.Tab().Token("}").Line()
   655  			}
   656  		}
   657  		// cache
   658  		cacheCmd, _, hasCache := function.Cache()
   659  		if hasCache && function.Result != nil {
   660  			if cacheCmd == "get" || cacheCmd == "get-set" {
   661  				body.Tab().Token("// cache get").Line()
   662  				body.Tab().Tab().Token("cached, cacheExist, cacheGetErr := caches.Load[").Add(result).Token("](ctx, param)").Line()
   663  				body.Tab().Token("if cacheGetErr != nil {").Line()
   664  				body.Tab().Tab().Token("log := logs.Load(ctx)", gcg.NewPackage("github.com/aacfactory/fns/logs")).Line()
   665  				body.Tab().Tab().Token("if log.WarnEnabled() {").Line()
   666  				body.Tab().Tab().Tab().Token("log.Warn().Cause(cacheGetErr).With(\"fns\", \"caches\").Message(\"fns: get cache failed\")").Line()
   667  				body.Tab().Tab().Token("}").Line()
   668  				body.Tab().Token("}").Line()
   669  				body.Tab().Token("if cacheExist {").Line()
   670  				body.Tab().Tab().Token("var promise futures.Promise").Line()
   671  				body.Tab().Tab().Token("promise, future = futures.New()").Line()
   672  				body.Tab().Tab().Token("promise.Succeed(services.NewResponse(cached))").Line()
   673  				body.Tab().Tab().Token("return").Line()
   674  				body.Tab().Token("}").Line()
   675  			}
   676  		}
   677  	}
   678  
   679  	// handle
   680  	body.Tab().Token("// handle").Line()
   681  	body.Tab().Token("eps := runtime.Endpoints(ctx)").Line()
   682  	if function.Param != nil {
   683  		body.Tab().Token(fmt.Sprintf("future, err = eps.RequestAsync(ctx, _endpointName, %s, param)", function.VarIdent)).Line()
   684  	} else {
   685  		body.Tab().Token(fmt.Sprintf("future, err = eps.RequestAsync(ctx, _endpointName, %s, nil)", function.VarIdent)).Line()
   686  	}
   687  	// return
   688  	body.Tab().Token("return")
   689  	// body <<<
   690  	proxy.Body(body)
   691  	code = proxy.Build()
   692  	return
   693  }
   694  
   695  func (s *ServiceFile) functionProxyCode(ctx context.Context, function *Function) (code gcg.Code, err error) {
   696  	proxyIdent := function.ProxyIdent
   697  	proxy := gcg.Func()
   698  	proxy.Name(proxyIdent)
   699  	proxy.AddParam("ctx", contextCode())
   700  	if function.Param != nil {
   701  		var param gcg.Code = nil
   702  		if s.service.Path == function.Param.Type.Path {
   703  			param = gcg.Ident(function.Param.Type.Name)
   704  		} else {
   705  			pkg, hasPKG := s.service.Imports.Path(function.Param.Type.Path)
   706  			if !hasPKG {
   707  				err = errors.Warning("modules: make function proxy code failed").
   708  					WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   709  					WithMeta("function", function.Name()).
   710  					WithCause(errors.Warning("import of param was not found").WithMeta("path", function.Param.Type.Path))
   711  				return
   712  			}
   713  			if pkg.Alias == "" {
   714  				param = gcg.QualifiedIdent(gcg.NewPackage(pkg.Path), function.Param.Type.Name)
   715  			} else {
   716  				param = gcg.QualifiedIdent(gcg.NewPackageWithAlias(pkg.Path, pkg.Alias), function.Param.Type.Name)
   717  			}
   718  		}
   719  		proxy.AddParam("param", param)
   720  	}
   721  	var result gcg.Code = nil
   722  	if function.Result != nil {
   723  		if s.service.Path == function.Result.Type.Path {
   724  			result = gcg.Ident(function.Result.Type.Name)
   725  		} else {
   726  			pkg, hasPKG := s.service.Imports.Path(function.Result.Type.Path)
   727  			if !hasPKG {
   728  				err = errors.Warning("modules: make function proxy code failed").
   729  					WithMeta("kind", "service").WithMeta("service", s.service.Name).WithMeta("file", s.Name()).
   730  					WithMeta("function", function.Name()).
   731  					WithCause(errors.Warning("import of result was not found").WithMeta("path", function.Result.Type.Path))
   732  				return
   733  			}
   734  			if pkg.Alias == "" {
   735  				result = gcg.QualifiedIdent(gcg.NewPackage(pkg.Path), function.Result.Type.Name)
   736  			} else {
   737  				result = gcg.QualifiedIdent(gcg.NewPackageWithAlias(pkg.Path, pkg.Alias), function.Result.Type.Name)
   738  			}
   739  		}
   740  		proxy.AddResult("result", result)
   741  	}
   742  	proxy.AddResult("err", gcg.Ident("error"))
   743  	// body >>>
   744  	body := gcg.Statements()
   745  	if function.Param != nil {
   746  		// validate
   747  		if validTitle, valid := function.Validation(); valid {
   748  			body.Tab().Token("// validate param").Line()
   749  			if validTitle == "" {
   750  				body.Tab().Token("if err = validators.Validate(param); err != nil {", gcg.NewPackage("github.com/aacfactory/fns/services/validators")).Line()
   751  				body.Tab().Tab().Token("return").Line()
   752  				body.Tab().Token("}").Line()
   753  			} else {
   754  				body.Tab().Token(fmt.Sprintf("if err = validators.ValidateWithErrorTitle(param, \"%s\"); err != nil {", validTitle)).Line()
   755  				body.Tab().Tab().Token("return").Line()
   756  				body.Tab().Token("}").Line()
   757  			}
   758  		}
   759  		// cache
   760  		cacheCmd, _, hasCache := function.Cache()
   761  		if hasCache && function.Result != nil {
   762  			if cacheCmd == "get" || cacheCmd == "get-set" {
   763  				body.Tab().Token("// cache get").Line()
   764  				body.Tab().Tab().Token("cached, cacheExist, cacheGetErr := caches.Load[").Add(result).Token("](ctx, param)").Line()
   765  				body.Tab().Token("if cacheGetErr != nil {").Line()
   766  				body.Tab().Tab().Token("log := logs.Load(ctx)", gcg.NewPackage("github.com/aacfactory/fns/logs")).Line()
   767  				body.Tab().Tab().Token("if log.WarnEnabled() {").Line()
   768  				body.Tab().Tab().Tab().Token("log.Warn().Cause(cacheGetErr).With(\"fns\", \"caches\").Message(\"fns: get cache failed\")").Line()
   769  				body.Tab().Tab().Token("}").Line()
   770  				body.Tab().Token("}").Line()
   771  				body.Tab().Token("if cacheExist {").Line()
   772  				body.Tab().Tab().Token("result = cached").Line()
   773  				body.Tab().Tab().Token("return").Line()
   774  				body.Tab().Token("}").Line()
   775  			}
   776  		}
   777  	}
   778  
   779  	// handle
   780  	body.Tab().Token("// handle").Line()
   781  	body.Tab().Token("eps := runtime.Endpoints(ctx)").Line()
   782  	if function.Param != nil {
   783  		if function.Result == nil {
   784  			body.Tab().Token(fmt.Sprintf("_, handleErr := eps.Request(ctx, _endpointName, %s, param)", function.VarIdent)).Line()
   785  		} else {
   786  			body.Tab().Token(fmt.Sprintf("response, handleErr := eps.Request(ctx, _endpointName, %s, param)", function.VarIdent)).Line()
   787  		}
   788  	} else {
   789  		if function.Result == nil {
   790  			body.Tab().Token(fmt.Sprintf("_, handleErr := eps.Request(ctx, _endpointName, %s, nil)", function.VarIdent)).Line()
   791  		} else {
   792  			body.Tab().Token(fmt.Sprintf("response, handleErr := eps.Request(ctx, _endpointName, %s, nil)", function.VarIdent)).Line()
   793  		}
   794  	}
   795  
   796  	body.Tab().Token("if handleErr != nil {").Line()
   797  	body.Tab().Tab().Token("err = handleErr").Line()
   798  	body.Tab().Tab().Token("return").Line()
   799  	body.Tab().Token("}").Line()
   800  	if function.Result != nil {
   801  		body.Tab().Token("result, err = services.ValueOfResponse[").Add(result).Token("](response)").Line()
   802  	}
   803  
   804  	// return
   805  	body.Tab().Token("return")
   806  	// body <<<
   807  	proxy.Body(body)
   808  	code = proxy.Build()
   809  	return
   810  }