github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/cmd/generates/modules/types.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 "bufio" 22 "bytes" 23 "context" 24 "fmt" 25 "github.com/aacfactory/errors" 26 "github.com/aacfactory/fns/cmd/generates/sources" 27 "github.com/aacfactory/gcg" 28 "strings" 29 ) 30 31 func mapTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 32 if ctx.Err() != nil { 33 err = errors.Warning("modules: mapping type to function document element code failed"). 34 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 35 WithCause(ctx.Err()) 36 return 37 } 38 switch typ.Kind { 39 case sources.BasicKind: 40 code, err = mapBasicTypeToFunctionElementCode(ctx, typ) 41 break 42 case sources.BuiltinKind: 43 code, err = mapBuiltinTypeToFunctionElementCode(ctx, typ) 44 break 45 case sources.IdentKind: 46 code, err = mapIdentTypeToFunctionElementCode(ctx, typ) 47 break 48 case sources.InterfaceKind: 49 code, err = mapInterfaceTypeToFunctionElementCode(ctx, typ) 50 break 51 case sources.StructKind: 52 code, err = mapStructTypeToFunctionElementCode(ctx, typ) 53 break 54 case sources.StructFieldKind: 55 code, err = mapStructFieldTypeToFunctionElementCode(ctx, typ) 56 break 57 case sources.PointerKind: 58 code, err = mapPointerTypeToFunctionElementCode(ctx, typ) 59 break 60 case sources.ArrayKind: 61 code, err = mapArrayTypeToFunctionElementCode(ctx, typ) 62 break 63 case sources.MapKind: 64 code, err = mapMapTypeToFunctionElementCode(ctx, typ) 65 break 66 case sources.AnyKind: 67 code, err = mapAnyTypeToFunctionElementCode(ctx, typ) 68 break 69 case sources.ParadigmKind: 70 code, err = mapParadigmTypeToFunctionElementCode(ctx, typ) 71 break 72 case sources.ParadigmElementKind: 73 code, err = mapParadigmElementTypeToFunctionElementCode(ctx, typ) 74 break 75 case sources.ReferenceKind: 76 code, err = mapReferenceTypeToFunctionElementCode(ctx, typ) 77 break 78 default: 79 err = errors.Warning("modules: mapping type to function document element code failed"). 80 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 81 WithCause(errors.Warning("unsupported kind")) 82 break 83 } 84 return 85 } 86 87 func mapBasicTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 88 if ctx.Err() != nil { 89 err = errors.Warning("modules: mapping basic type to function document element code failed"). 90 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 91 WithCause(ctx.Err()) 92 return 93 } 94 stmt := gcg.Statements() 95 switch typ.Name { 96 case "string": 97 stmt.Token(fmt.Sprintf("documents.String()")) 98 break 99 case "bool": 100 stmt.Token(fmt.Sprintf("documents.Bool()")) 101 break 102 case "int8", "int16", "int32": 103 stmt.Token(fmt.Sprintf("documents.Int32()")) 104 break 105 case "int", "int64": 106 stmt.Token(fmt.Sprintf("documents.Int64()")) 107 break 108 case "uint8", "byte": 109 stmt.Token(fmt.Sprintf("documents.Uint8()")) 110 break 111 case "uint16", "uint32": 112 stmt.Token(fmt.Sprintf("documents.Uint32()")) 113 break 114 case "uint", "uint64": 115 stmt.Token(fmt.Sprintf("documents.Uint64()")) 116 break 117 case "float32": 118 stmt.Token(fmt.Sprintf("documents.Float32()")) 119 break 120 case "float64": 121 stmt.Token(fmt.Sprintf("documents.Float64()")) 122 break 123 case "complex64": 124 stmt.Token(fmt.Sprintf("documents.Complex64()")) 125 break 126 case "complex128": 127 stmt.Token(fmt.Sprintf("documents.Complex128()")) 128 break 129 default: 130 if typ.Path == "time" && typ.Name == "Time" { 131 stmt.Token(fmt.Sprintf("documents.DateTime()")) 132 break 133 } 134 if typ.Path == "time" && typ.Name == "Duration" { 135 stmt.Token(fmt.Sprintf("documents.Duration()")) 136 break 137 } 138 if typ.Path == "github.com/aacfactory/fns/commons/passwords" && typ.Name == "Password" { 139 stmt.Token(fmt.Sprintf("documents.Password()")) 140 break 141 } 142 if typ.Path == "github.com/aacfactory/json" && typ.Name == "Date" { 143 stmt.Token(fmt.Sprintf("documents.Date()")) 144 break 145 } 146 if typ.Path == "github.com/aacfactory/json" && typ.Name == "Time" { 147 stmt.Token(fmt.Sprintf("documents.Time()")) 148 break 149 } 150 if typ.Path == "github.com/aacfactory/fns/commons/times" && typ.Name == "Date" { 151 stmt.Token(fmt.Sprintf("documents.Date()")) 152 break 153 } 154 if typ.Path == "github.com/aacfactory/fns/commons/times" && typ.Name == "Time" { 155 stmt.Token(fmt.Sprintf("documents.Time()")) 156 break 157 } 158 if typ.Path == "encoding/json" && typ.Name == "RawMessage" { 159 stmt.Token(fmt.Sprintf("documents.JsonRaw()")) 160 break 161 } 162 if typ.Path == "github.com/aacfactory/json" && typ.Name == "RawMessage" { 163 stmt.Token(fmt.Sprintf("documents.JsonRaw()")) 164 break 165 } 166 err = errors.Warning("modules: unsupported basic type").WithMeta("name", typ.Name) 167 return 168 } 169 code = stmt 170 return 171 } 172 173 func mapBuiltinTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 174 if ctx.Err() != nil { 175 err = errors.Warning("modules: mapping builtin type to function document element code failed"). 176 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 177 WithCause(ctx.Err()) 178 return 179 } 180 code = gcg.Statements().Token("documents.Ref(").Token(fmt.Sprintf("\"%s\",\"%s\"", typ.Path, typ.Name)).Symbol(")") 181 return 182 } 183 184 func mapIdentTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 185 if ctx.Err() != nil { 186 err = errors.Warning("modules: mapping ident type to function document element code failed"). 187 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 188 WithCause(ctx.Err()) 189 return 190 } 191 targetCode, targetCodeErr := mapTypeToFunctionElementCode(ctx, typ.Elements[0]) 192 if targetCodeErr != nil { 193 err = errors.Warning("modules: mapping ident type to function element code failed"). 194 WithMeta("name", typ.Name).WithMeta("path", typ.Path). 195 WithCause(targetCodeErr) 196 return 197 } 198 code = gcg.Statements().Token("documents.Ident(").Line(). 199 Token(fmt.Sprintf("\"%s\",\"%s\"", typ.Path, typ.Name)).Symbol(",").Line(). 200 Add(targetCode).Symbol(",").Line(). 201 Symbol(")") 202 return 203 } 204 205 func mapInterfaceTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 206 if ctx.Err() != nil { 207 err = errors.Warning("modules: mapping interface type to function document element code failed"). 208 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 209 WithCause(ctx.Err()) 210 return 211 } 212 stmt := gcg.Statements() 213 stmt = stmt.Token("documents.Struct(").Token(fmt.Sprintf("\"%s\",\"%s\"", typ.Path, typ.Name)).Symbol(")") 214 title, hasTitle := typ.Annotations.Value("title") 215 if hasTitle { 216 stmt = stmt.Dot().Line().Token("SetTitle(").Token(fmt.Sprintf("\"%s\"", strings.ReplaceAll(title, "\n", "\\n"))).Symbol(")") 217 } 218 description, hasDescription := typ.Annotations.Value("description") 219 if hasDescription { 220 stmt = stmt.Dot().Line().Token("SetDescription(").Token(fmt.Sprintf("\"%s\"", description)).Symbol(")") 221 } 222 _, hasDeprecated := typ.Annotations.Get("deprecated") 223 if hasDeprecated { 224 stmt = stmt.Dot().Line().Token("AsDeprecated()") 225 } 226 code = stmt 227 return 228 } 229 230 func mapPointerTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 231 if ctx.Err() != nil { 232 err = errors.Warning("modules: mapping pointer type to function document element code failed"). 233 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 234 WithCause(ctx.Err()) 235 return 236 } 237 code, err = mapTypeToFunctionElementCode(ctx, typ.Elements[0]) 238 return 239 } 240 241 func mapStructTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 242 if ctx.Err() != nil { 243 err = errors.Warning("modules: mapping struct type to function document element code failed"). 244 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 245 WithCause(ctx.Err()) 246 return 247 } 248 if typ.ParadigmsPacked != nil { 249 typ = typ.ParadigmsPacked 250 } 251 stmt := gcg.Statements() 252 stmt = stmt.Token("documents.Struct(").Token(fmt.Sprintf("\"%s\",\"%s\"", typ.Path, typ.Name)).Symbol(")") 253 title, hasTitle := typ.Annotations.Value("title") 254 if hasTitle { 255 stmt = stmt.Dot().Line().Token("SetTitle(").Token(fmt.Sprintf("\"%s\"", strings.ReplaceAll(title, "\n", "\\n"))).Symbol(")") 256 } 257 description, hasDescription := typ.Annotations.Value("description") 258 if hasDescription { 259 stmt = stmt.Dot().Line().Token("SetDescription(").Token(fmt.Sprintf("\"%s\"", description)).Symbol(")") 260 } 261 _, hasDeprecated := typ.Annotations.Get("deprecated") 262 if hasDeprecated { 263 stmt = stmt.Dot().Line().Token("AsDeprecated()") 264 } 265 for _, field := range typ.Elements { 266 name, hasName := field.Tags["json"] 267 if !hasName { 268 name = field.Name 269 } 270 if name == "-" { 271 continue 272 } 273 fieldCode, fieldCodeErr := mapTypeToFunctionElementCode(ctx, field) 274 if fieldCodeErr != nil { 275 err = errors.Warning("modules: mapping struct type to function element code failed"). 276 WithMeta("name", typ.Name).WithMeta("path", typ.Path). 277 WithMeta("field", typ.Name). 278 WithCause(fieldCodeErr) 279 return 280 } 281 stmt = stmt.Dot().Line(). 282 Token("AddProperty(").Line(). 283 Token(fmt.Sprintf("\"%s\"", name)).Symbol(",").Line(). 284 Add(fieldCode).Symbol(",").Line(). 285 Symbol(")") 286 } 287 code = stmt 288 return 289 } 290 291 func mapStructFieldTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 292 if ctx.Err() != nil { 293 err = errors.Warning("modules: mapping struct field type to function document element code failed"). 294 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 295 WithCause(ctx.Err()) 296 return 297 } 298 elementCode, elementCodeErr := mapTypeToFunctionElementCode(ctx, typ.Elements[0]) 299 if elementCodeErr != nil { 300 err = errors.Warning("modules: mapping struct field type to function element code failed"). 301 WithMeta("name", typ.Name).WithMeta("path", typ.Path). 302 WithMeta("field", typ.Name). 303 WithCause(elementCodeErr) 304 return 305 } 306 stmt := elementCode.(*gcg.Statement) 307 fieldTitle, hasFieldTitle := typ.Annotations.Value("title") 308 if hasFieldTitle { 309 stmt = stmt.Dot().Line().Token("SetTitle(").Token(fmt.Sprintf("\"%s\"", strings.ReplaceAll(fieldTitle, "\n", "\\n"))).Symbol(")") 310 } 311 fieldDescription, hasFieldDescription := typ.Annotations.Value("description") 312 if hasFieldDescription { 313 stmt = stmt.Dot().Line().Token("SetDescription(").Token(fmt.Sprintf("\"%s\"", fieldDescription)).Symbol(")") 314 } 315 _, hasFieldDeprecated := typ.Annotations.Get("deprecated") 316 if hasFieldDeprecated { 317 stmt = stmt.Dot().Line().Token("AsDeprecated()") 318 } 319 // password 320 _, hasFieldPassword := typ.Annotations.Get("password") 321 if hasFieldPassword { 322 stmt = stmt.Dot().Line().Token("AsPassword()") 323 } 324 // enum 325 fieldEnum, hasFieldEnum := typ.Annotations.Get("enum") 326 if hasFieldEnum && len(fieldEnum.Params) > 0 { 327 fieldEnums := strings.Split(fieldEnum.Params[0], ",") 328 fieldEnumsCodeToken := "" 329 for _, enumValue := range fieldEnums { 330 fieldEnumsCodeToken = fieldEnumsCodeToken + `, "` + strings.TrimSpace(enumValue) + `"` 331 } 332 if fieldEnumsCodeToken != "" { 333 fieldEnumsCodeToken = fieldEnumsCodeToken[2:] 334 stmt = stmt.Dot().Line().Token("AddEnum").Symbol("(").Token(fieldEnumsCodeToken).Symbol(")") 335 } 336 } 337 // validation 338 fieldValidate, hasFieldValidate := typ.Tags["validate"] 339 if hasFieldValidate && fieldValidate != "" { 340 fieldRequired := strings.Contains(fieldValidate, "required") 341 if fieldRequired { 342 stmt = stmt.Dot().Line().Token("AsRequired()") 343 } 344 fieldValidateMessage, hasFieldValidateMessage := typ.Tags["validate-message"] 345 if !hasFieldValidateMessage { 346 fieldValidateMessage = typ.Tags["message"] 347 } 348 349 fieldValidateMessageI18ns := make([]string, 0, 1) 350 validateMessageI18n, hasValidateMessageI18n := typ.Annotations.Get("validate-message-i18n") 351 if hasValidateMessageI18n && len(validateMessageI18n.Params) > 0 { 352 reader := bufio.NewReader(bytes.NewReader([]byte(validateMessageI18n.Params[0]))) 353 for { 354 line, _, readErr := reader.ReadLine() 355 if readErr != nil { 356 break 357 } 358 idx := bytes.IndexByte(line, ':') 359 if idx > 0 && idx < len(line) { 360 fieldValidateMessageI18ns = append(fieldValidateMessageI18ns, strings.TrimSpace(string(line[0:idx]))) 361 fieldValidateMessageI18ns = append(fieldValidateMessageI18ns, strings.TrimSpace(string(line[idx+1:]))) 362 } 363 } 364 } 365 fieldValidateMessageI18nsCodeToken := "" 366 for i := 0; i < len(fieldValidateMessageI18ns); i = i + 2 { 367 key := fieldValidateMessageI18ns[i] 368 val := fieldValidateMessageI18ns[i+1] 369 fieldValidateMessageI18nsCodeToken = fieldValidateMessageI18nsCodeToken + ".AddI18n(" + "\"" + key + "\", " + "\"" + val + "\"" + ")" 370 } 371 stmt = stmt.Dot().Line().Token("SetValidation("). 372 Token("documents.NewValidation("). 373 Token(fmt.Sprintf("\"%s\"", fieldValidateMessage)). 374 Symbol(")"). 375 Token(fieldValidateMessageI18nsCodeToken). 376 Symbol(")") 377 } 378 code = stmt 379 return 380 } 381 382 func mapArrayTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 383 if ctx.Err() != nil { 384 err = errors.Warning("modules: mapping array type to function document element code failed"). 385 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 386 WithCause(ctx.Err()) 387 return 388 } 389 element := typ.Elements[0] 390 name, isBasic := element.Basic() 391 if isBasic && (name == "byte" || name == "uint8") { 392 stmt := gcg.Statements() 393 stmt = stmt.Token(fmt.Sprintf("documents.Bytes()")) 394 code = stmt 395 return 396 } 397 elementCode, elementCodeErr := mapTypeToFunctionElementCode(ctx, element) 398 if elementCodeErr != nil { 399 err = errors.Warning("modules: mapping array type to function element code failed"). 400 WithMeta("name", typ.Name).WithMeta("path", typ.Path). 401 WithCause(elementCodeErr) 402 return 403 } 404 stmt := gcg.Statements() 405 stmt = stmt.Token("documents.Array(").Add(elementCode).Symbol(")") 406 if typ.Path != "" && typ.Name != "" { 407 stmt = stmt.Dot().Line().Token(fmt.Sprintf("SetPath(\"%s\")", typ.Path)) 408 stmt = stmt.Dot().Line().Token(fmt.Sprintf("SetName(\"%s\")", typ.Name)) 409 } 410 title, hasTitle := typ.Annotations.Value("title") 411 if hasTitle { 412 stmt = stmt.Dot().Line().Token("SetTitle(").Token(fmt.Sprintf("\"%s\"", strings.ReplaceAll(title, "\n", "\\n"))).Symbol(")") 413 } 414 description, hasDescription := typ.Annotations.Value("description") 415 if hasDescription { 416 stmt = stmt.Dot().Line().Token("SetDescription(").Token(fmt.Sprintf("\"%s\"", description)).Symbol(")") 417 } 418 _, hasDeprecated := typ.Annotations.Get("deprecated") 419 if hasDeprecated { 420 stmt = stmt.Dot().Line().Token("AsDeprecated()") 421 } 422 code = stmt 423 return 424 } 425 426 func mapMapTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 427 if ctx.Err() != nil { 428 err = errors.Warning("modules: mapping map type to function document element code failed"). 429 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 430 WithCause(ctx.Err()) 431 return 432 } 433 element := typ.Elements[1] 434 elementCode, elementCodeErr := mapTypeToFunctionElementCode(ctx, element) 435 if elementCodeErr != nil { 436 err = errors.Warning("modules: mapping map type to function element code failed"). 437 WithMeta("name", typ.Name).WithMeta("path", typ.Path). 438 WithCause(elementCodeErr) 439 return 440 } 441 stmt := gcg.Statements() 442 stmt = stmt.Token("documents.Map(").Add(elementCode).Symbol(")") 443 if typ.Path != "" && typ.Name != "" { 444 stmt = stmt.Dot().Line().Token(fmt.Sprintf("SetPath(\"%s\")", typ.Path)) 445 stmt = stmt.Dot().Line().Token(fmt.Sprintf("SetName(\"%s\")", typ.Name)) 446 } 447 title, hasTitle := typ.Annotations.Value("title") 448 if hasTitle { 449 stmt = stmt.Dot().Line().Token("SetTitle(").Token(fmt.Sprintf("\"%s\"", strings.ReplaceAll(title, "\n", "\\n"))).Symbol(")") 450 } 451 description, hasDescription := typ.Annotations.Value("description") 452 if hasDescription { 453 stmt = stmt.Dot().Line().Token("SetDescription(").Token(fmt.Sprintf("\"%s\"", description)).Symbol(")") 454 } 455 _, hasDeprecated := typ.Annotations.Get("deprecated") 456 if hasDeprecated { 457 stmt = stmt.Dot().Line().Token("AsDeprecated()") 458 } 459 code = stmt 460 return 461 } 462 463 func mapAnyTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 464 if ctx.Err() != nil { 465 err = errors.Warning("modules: mapping any type to function document element code failed"). 466 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 467 WithCause(ctx.Err()) 468 return 469 } 470 code = gcg.Statements().Token("documents.Any()") 471 return 472 } 473 474 func mapParadigmTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 475 if ctx.Err() != nil { 476 err = errors.Warning("modules: mapping paradigm type to function document element code failed"). 477 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 478 WithCause(ctx.Err()) 479 return 480 } 481 code, err = mapTypeToFunctionElementCode(ctx, typ.ParadigmsPacked) 482 if err != nil { 483 err = errors.Warning("modules: mapping paradigm type to function document element code failed"). 484 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 485 WithCause(err) 486 return 487 } 488 return 489 } 490 491 func mapParadigmElementTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 492 if ctx.Err() != nil { 493 err = errors.Warning("modules: mapping paradigm element type to function document element code failed"). 494 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 495 WithCause(ctx.Err()) 496 return 497 } 498 code, err = mapTypeToFunctionElementCode(ctx, typ.ParadigmsPacked) 499 if err != nil { 500 err = errors.Warning("modules: mapping paradigm element type to function document element code failed"). 501 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 502 WithCause(err) 503 return 504 } 505 return 506 } 507 508 func mapReferenceTypeToFunctionElementCode(ctx context.Context, typ *sources.Type) (code gcg.Code, err error) { 509 if ctx.Err() != nil { 510 err = errors.Warning("modules: mapping reference type to function document element code failed"). 511 WithMeta("path", typ.Path).WithMeta("name", typ.Name).WithMeta("kind", typ.Kind.String()). 512 WithCause(ctx.Err()) 513 return 514 } 515 code = gcg.Statements().Token(fmt.Sprintf("documents.Ref(\"%s\", \"%s\")", typ.Path, typ.Name)) 516 return 517 }