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 }