github.com/qiniu/go-sdk/v7@v7.27.0/internal/api-generator/json.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/dave/jennifer/jen" 8 "github.com/iancoleman/strcase" 9 "gopkg.in/yaml.v3" 10 ) 11 12 type ( 13 JsonType struct { 14 String bool 15 Integer bool 16 Float bool 17 Boolean bool 18 Array *JsonArray 19 Struct *JsonStruct 20 Any bool 21 StringMap bool 22 } 23 24 JsonArray struct { 25 Type *JsonType `yaml:"type,omitempty"` 26 Name string `yaml:"name,omitempty"` 27 CamelCaseName string `yaml:"camel_case_name,omitempty"` 28 SnakeCaseName string `yaml:"snake_case_name,omitempty"` 29 Documentation string `yaml:"documentation,omitempty"` 30 } 31 32 JsonStruct struct { 33 Fields []JsonField `yaml:"fields,omitempty"` 34 Name string `yaml:"name,omitempty"` 35 CamelCaseName string `yaml:"camel_case_name,omitempty"` 36 SnakeCaseName string `yaml:"snake_case_name,omitempty"` 37 Documentation string `yaml:"documentation,omitempty"` 38 } 39 40 JsonField struct { 41 Type JsonType `yaml:"type,omitempty"` 42 Key string `yaml:"key,omitempty"` 43 FieldName string `yaml:"field_name,omitempty"` 44 FieldCamelCaseName string `yaml:"field_camel_case_name,omitempty"` 45 FieldSnakeCaseName string `yaml:"field_snake_case_name,omitempty"` 46 Documentation string `yaml:"documentation,omitempty"` 47 Optional *OptionalType `yaml:"optional,omitempty"` 48 ServiceBucket *ServiceBucketType `yaml:"service_bucket,omitempty"` 49 ServiceObject *ServiceObjectType `yaml:"service_object,omitempty"` 50 } 51 ) 52 53 func (jsonField *JsonField) camelCaseName() string { 54 if jsonField.FieldCamelCaseName != "" { 55 return jsonField.FieldCamelCaseName 56 } 57 return strcase.ToCamel(jsonField.FieldName) 58 } 59 60 func (jsonStruct *JsonStruct) camelCaseName() string { 61 if jsonStruct.CamelCaseName != "" { 62 return jsonStruct.CamelCaseName 63 } 64 return strcase.ToCamel(jsonStruct.Name) 65 } 66 67 func (jsonStruct *JsonStruct) addFields(group *jen.Group, includesJsonTag bool) error { 68 for _, field := range jsonStruct.Fields { 69 code, err := field.Type.AddTypeToStatement(jen.Id(field.camelCaseName()), field.Optional.ToOptionalType() == OptionalTypeNullable) 70 if err != nil { 71 return err 72 } 73 if includesJsonTag { 74 jsonTag := field.Key 75 switch field.Optional.ToOptionalType() { 76 case OptionalTypeOmitEmpty, OptionalTypeNullable: 77 jsonTag += ",omitempty" 78 } 79 code = code.Tag(map[string]string{"json": jsonTag}) 80 } 81 if field.Documentation != "" { 82 code = code.Comment(field.Documentation) 83 } 84 group.Add(code) 85 } 86 return nil 87 } 88 89 func (jsonStruct *JsonStruct) addGetBucketNameFunc(group *jen.Group, structName string) (bool, error) { 90 field := jsonStruct.getServiceBucketField() 91 92 if field == nil || field.ServiceBucket.ToServiceBucketType() == ServiceBucketTypeNone { 93 return false, nil 94 } else if !field.Type.String { 95 panic("service bucket field must be string") 96 } 97 98 group.Add(jen.Func(). 99 Params(jen.Id("j").Op("*").Id(structName)). 100 Id("getBucketName"). 101 Params(jen.Id("ctx").Qual("context", "Context")). 102 Params(jen.String(), jen.Error()). 103 BlockFunc(func(group *jen.Group) { 104 fieldName := field.camelCaseName() 105 switch field.ServiceBucket.ToServiceBucketType() { 106 case ServiceBucketTypePlainText: 107 group.Add(jen.Return(jen.Id("j").Dot(fieldName), jen.Nil())) 108 case ServiceBucketTypeEntry: 109 group.Add( 110 jen.Return( 111 jen.Qual("strings", "SplitN"). 112 Call(jen.Id("j").Dot(fieldName), jen.Lit(":"), jen.Lit(2)). 113 Index(jen.Lit(0)), 114 jen.Nil(), 115 ), 116 ) 117 case ServiceBucketTypeUploadToken: 118 group.Add( 119 jen.If(jen.Id("putPolicy"), jen.Err()). 120 Op(":="). 121 Qual(PackageNameUpToken, "NewParser"). 122 Call(jen.Id("j").Dot(fieldName)). 123 Dot("GetPutPolicy"). 124 Call(jen.Id("ctx")). 125 Op(";"). 126 Err(). 127 Op("!="). 128 Nil(). 129 BlockFunc(func(group *jen.Group) { 130 group.Add(jen.Return(jen.Lit(""), jen.Err())) 131 }). 132 Else(). 133 BlockFunc(func(group *jen.Group) { 134 group.Add(jen.Return(jen.Id("putPolicy").Dot("GetBucketName").Call())) 135 }), 136 ) 137 default: 138 panic("unknown ServiceBucketType") 139 } 140 })) 141 return true, nil 142 } 143 144 func (jsonStruct *JsonStruct) addGetObjectNameFunc(group *jen.Group, structName string) (bool, error) { 145 field := jsonStruct.getServiceObjectField() 146 147 if field == nil || field.ServiceObject.ToServiceObjectType() == ServiceObjectTypeNone { 148 return false, nil 149 } else if !field.Type.String { 150 panic("service object field must be string") 151 } 152 153 group.Add(jen.Func(). 154 Params(jen.Id("j").Op("*").Id(structName)). 155 Id("getObjectName"). 156 Params(). 157 Params(jen.String()). 158 BlockFunc(func(group *jen.Group) { 159 fieldName := field.camelCaseName() 160 switch field.ServiceObject.ToServiceObjectType() { 161 case ServiceObjectTypePlainText: 162 if field.Optional.ToOptionalType() == OptionalTypeNullable { 163 group.Add(jen.Var().Id("objectName").String()) 164 group.Add(jen.If(jen.Id("j").Dot(fieldName).Op("!=").Nil()).BlockFunc(func(group *jen.Group) { 165 group.Add(jen.Id("objectName").Op("=").Op("*").Id("j").Dot(fieldName)) 166 })) 167 group.Add(jen.Return(jen.Id("objectName"))) 168 } else { 169 group.Return(jen.Id("j").Dot(fieldName)) 170 } 171 case ServiceObjectTypeEntry: 172 group.Add( 173 jen.Id("parts"). 174 Op(":="). 175 Qual("strings", "SplitN"). 176 Call(jen.Id("j").Dot(fieldName), jen.Lit(":"), jen.Lit(2))) 177 group.Add(jen.If(jen.Len(jen.Id("parts")).Op(">").Lit(1)), jen.BlockFunc(func(group *jen.Group) { 178 group.Return(jen.Id("parts").Index(jen.Lit(1))) 179 })) 180 group.Add(jen.Return(jen.Lit(""))) 181 default: 182 panic("unknown ServiceObjectType") 183 } 184 })) 185 return true, nil 186 } 187 188 func (jsonType *JsonType) generate(group *jen.Group, options CodeGeneratorOptions) error { 189 return jsonType.generateType(group, options, true, func() error { 190 if jsonType.Any { 191 return jsonType.addAnyJsonMarshalerUnmarshaler(group, options.camelCaseName()) 192 } 193 return errors.New("base type could not be top level") 194 }) 195 } 196 197 func (jsonType *JsonType) generateType(group *jen.Group, options CodeGeneratorOptions, topLevel bool, otherWise func() error) error { 198 if s := jsonType.Struct; s != nil { 199 if err := s.generate(group, options, topLevel); err != nil { 200 return err 201 } 202 return nil 203 } else if a := jsonType.Array; a != nil { 204 if err := a.generate(group, options, topLevel); err != nil { 205 return err 206 } 207 return nil 208 } 209 return otherWise() 210 } 211 212 func (jsonType *JsonType) AddTypeToStatement(statement *jen.Statement, nilable bool) (*jen.Statement, error) { 213 if nilable { 214 statement = statement.Op("*") 215 } 216 if jsonType.String { 217 return statement.String(), nil 218 } else if jsonType.Integer { 219 return statement.Int64(), nil 220 } else if jsonType.Float { 221 return statement.Float64(), nil 222 } else if jsonType.Boolean { 223 return statement.Bool(), nil 224 } else if jsonType.Any { 225 return statement.Interface(), nil 226 } else if jsonType.StringMap { 227 return statement.Map(jen.String()).String(), nil 228 } else if jsonType.Array != nil { 229 return statement.Id(jsonType.Array.camelCaseName()), nil 230 } else if jsonType.Struct != nil { 231 return statement.Id(jsonType.Struct.camelCaseName()), nil 232 } else { 233 return nil, errors.New("unknown type") 234 } 235 } 236 237 func (jsonType *JsonType) ZeroValue() interface{} { 238 if jsonType.String { 239 return "" 240 } else if jsonType.Integer { 241 return 0 242 } else if jsonType.Float { 243 return 0.0 244 } else if jsonType.Boolean { 245 return false 246 } else { 247 return nil 248 } 249 } 250 251 func (jsonType *JsonType) addAnyJsonMarshalerUnmarshaler(group *jen.Group, structName string) (err error) { 252 group.Add( 253 jen.Func(). 254 Params(jen.Id("j").Op("*").Id(structName)). 255 Id("MarshalJSON"). 256 Params(). 257 Params(jen.Index().Byte(), jen.Error()). 258 BlockFunc(func(group *jen.Group) { 259 group.Add(jen.Return( 260 jen.Qual("encoding/json", "Marshal"). 261 Call(jen.Id("j").Dot("Body")), 262 )) 263 }), 264 ) 265 group.Add( 266 jen.Func(). 267 Params(jen.Id("j").Op("*").Id(structName)). 268 Id("UnmarshalJSON"). 269 Params(jen.Id("data").Index().Byte()). 270 Params(jen.Error()). 271 BlockFunc(func(group *jen.Group) { 272 group.Return().Qual("encoding/json", "Unmarshal").Call(jen.Id("data"), jen.Op("&").Id("j").Dot("Body")) 273 }), 274 ) 275 return 276 } 277 278 func (jsonArray *JsonArray) camelCaseName() string { 279 if jsonArray.CamelCaseName != "" { 280 return jsonArray.CamelCaseName 281 } 282 return strcase.ToCamel(jsonArray.Name) 283 } 284 285 func (jsonArray *JsonArray) addFields(group *jen.Group) error { 286 code := jen.Id(jsonArray.camelCaseName()).Id(jsonArray.camelCaseName()) 287 if jsonArray.Documentation != "" { 288 code = code.Comment(jsonArray.Documentation) 289 } 290 group.Add(code) 291 return nil 292 } 293 294 func (jsonArray *JsonArray) generate(group *jen.Group, options CodeGeneratorOptions, topLevel bool) (err error) { 295 if err = jsonArray.Type.generateType(group, CodeGeneratorOptions{}, false, func() error { 296 return nil 297 }); err != nil { 298 return 299 } 300 301 if jsonArray.Documentation != "" { 302 group.Add(jen.Comment(jsonArray.Documentation)) 303 } 304 code := jen.Type().Id(jsonArray.camelCaseName()) 305 if !topLevel { 306 code = code.Op("=") 307 } 308 code = code.Index() 309 code, err = jsonArray.Type.AddTypeToStatement(code, false) 310 if err != nil { 311 return 312 } 313 group.Add(code) 314 if topLevel { 315 if err = jsonArray.addJsonMarshalerUnmarshaler(group, options.camelCaseName()); err != nil { 316 return 317 } 318 } 319 320 return 321 } 322 323 func (jsonArray *JsonArray) addJsonMarshalerUnmarshaler(group *jen.Group, structName string) (err error) { 324 fieldName := jsonArray.camelCaseName() 325 group.Add( 326 jen.Func(). 327 Params(jen.Id("j").Op("*").Id(structName)). 328 Id("MarshalJSON"). 329 Params(). 330 Params(jen.Index().Byte(), jen.Error()). 331 BlockFunc(func(group *jen.Group) { 332 group.Add(jen.Return( 333 jen.Qual("encoding/json", "Marshal"). 334 Call(jen.Id("j").Dot(fieldName)), 335 )) 336 }), 337 ) 338 group.Add( 339 jen.Func(). 340 Params(jen.Id("j").Op("*").Id(structName)). 341 Id("UnmarshalJSON"). 342 Params(jen.Id("data").Index().Byte()). 343 Params(jen.Error()). 344 BlockFunc(func(group *jen.Group) { 345 group.Add(jen.Var().Id("array").Id(fieldName)) 346 group.Add( 347 jen.If( 348 jen.Err().Op(":=").Qual("encoding/json", "Unmarshal").Call(jen.Id("data"), jen.Op("&").Id("array")), 349 jen.Err().Op("!=").Nil(), 350 ).BlockFunc(func(group *jen.Group) { 351 group.Return(jen.Err()) 352 }), 353 ) 354 group.Add(jen.Id("j").Dot(fieldName).Op("=").Id("array")) 355 group.Add(jen.Return(jen.Nil())) 356 }), 357 ) 358 return 359 } 360 361 func (jsonStruct *JsonStruct) generate(group *jen.Group, options CodeGeneratorOptions, topLevel bool) (err error) { 362 for _, field := range jsonStruct.Fields { 363 if err = field.Type.generateType(group, CodeGeneratorOptions{ 364 Name: field.FieldName, 365 CamelCaseName: field.FieldCamelCaseName, 366 SnakeCaseName: field.FieldSnakeCaseName, 367 Documentation: field.Documentation, 368 }, false, func() error { 369 return nil 370 }); err != nil { 371 return 372 } 373 } 374 375 opts := make([]CodeGeneratorOptions, 0, 2) 376 if options.camelCaseName() != "" { 377 opts = append(opts, options) 378 } 379 if jsonStruct.camelCaseName() != "" && options.camelCaseName() != jsonStruct.camelCaseName() { 380 opts = append(opts, CodeGeneratorOptions{ 381 Name: jsonStruct.Name, 382 CamelCaseName: jsonStruct.CamelCaseName, 383 SnakeCaseName: jsonStruct.SnakeCaseName, 384 Documentation: jsonStruct.Documentation, 385 }) 386 } 387 if len(opts) == 0 { 388 return errors.New("unknown struct name") 389 } 390 391 if !topLevel { 392 if opts[0].Documentation != "" { 393 group.Add(jen.Comment(opts[0].Documentation)) 394 } 395 group.Add(jen.Type().Id(opts[0].camelCaseName()).StructFunc(func(group *jen.Group) { 396 err = jsonStruct.addFields(group, false) 397 })) 398 if err != nil { 399 return 400 } 401 } 402 403 if len(opts) > 1 { 404 if opts[1].Documentation != "" { 405 group.Add(jen.Comment(opts[1].Documentation)) 406 } 407 group.Add(jen.Type().Id(opts[1].camelCaseName()).Op("=").Id(opts[0].camelCaseName())) 408 } 409 410 if err = jsonStruct.addJsonMarshalerUnmarshaler(group, opts[0].camelCaseName()); err != nil { 411 return 412 } 413 if err = jsonStruct.generateValidateFunc(group, opts[0].camelCaseName()); err != nil { 414 return 415 } 416 417 return 418 } 419 420 func (jsonStruct *JsonStruct) addJsonMarshalerUnmarshaler(group *jen.Group, structName string) (err error) { 421 group.Add(jen.Type().Id("json" + structName).StructFunc(func(group *jen.Group) { 422 err = jsonStruct.addFields(group, true) 423 })) 424 if err != nil { 425 return 426 } 427 group.Add( 428 jen.Func(). 429 Params(jen.Id("j").Op("*").Id(structName)). 430 Id("MarshalJSON"). 431 Params(). 432 Params(jen.Index().Byte(), jen.Error()). 433 BlockFunc(func(group *jen.Group) { 434 group.Add(jen.If( 435 jen.Err().Op(":=").Id("j").Dot("validate").Call(), 436 jen.Err().Op("!=").Nil(), 437 ).BlockFunc(func(group *jen.Group) { 438 group.Return(jen.Nil(), jen.Err()) 439 })) 440 group.Add(jen.Return( 441 jen.Qual("encoding/json", "Marshal"). 442 Call( 443 jen.Op("&").Id("json" + structName). 444 ValuesFunc(func(group *jen.Group) { 445 for _, field := range jsonStruct.Fields { 446 fieldName := field.camelCaseName() 447 group.Add(jen.Id(fieldName).Op(":").Id("j").Dot(fieldName)) 448 } 449 }), 450 ), 451 )) 452 }), 453 ) 454 group.Add( 455 jen.Func(). 456 Params(jen.Id("j").Op("*").Id(structName)). 457 Id("UnmarshalJSON"). 458 Params(jen.Id("data").Index().Byte()). 459 Params(jen.Error()). 460 BlockFunc(func(group *jen.Group) { 461 group.Add(jen.Var().Id("nj").Id("json" + structName)) 462 group.Add( 463 jen.If( 464 jen.Err().Op(":=").Qual("encoding/json", "Unmarshal").Call(jen.Id("data"), jen.Op("&").Id("nj")), 465 jen.Err().Op("!=").Nil(), 466 ).BlockFunc(func(group *jen.Group) { 467 group.Return(jen.Err()) 468 }), 469 ) 470 for _, field := range jsonStruct.Fields { 471 fieldName := field.camelCaseName() 472 group.Add(jen.Id("j").Dot(fieldName).Op("=").Id("nj").Dot(fieldName)) 473 } 474 group.Add(jen.Return(jen.Nil())) 475 }), 476 ) 477 return 478 } 479 480 func (jsonStruct *JsonStruct) generateValidateFunc(group *jen.Group, structName string) error { 481 group.Add(jen.Func(). 482 Params(jen.Id("j").Op("*").Id(structName)). 483 Id("validate"). 484 Params(). 485 Params(jen.Error()). 486 BlockFunc(func(group *jen.Group) { 487 for _, field := range jsonStruct.Fields { 488 if field.Optional.ToOptionalType() == OptionalTypeRequired { 489 var cond *jen.Statement 490 fieldName := field.camelCaseName() 491 if field.Type.String || field.Type.Integer || field.Type.Float { 492 cond = jen.Id("j").Dot(fieldName).Op("==").Lit(field.Type.ZeroValue()) 493 } else if field.Type.Boolean { 494 // do nothing 495 } else if field.Type.Array != nil { 496 cond = jen.Len(jen.Id("j").Dot(fieldName)).Op("==").Lit(0) 497 } else if field.Type.Struct != nil { 498 // do nothing 499 } else { 500 cond = jen.Id("j").Dot(fieldName).Op("==").Nil() 501 } 502 if cond != nil { 503 group.Add(jen.If(cond).BlockFunc(func(group *jen.Group) { 504 group.Add(jen.Return( 505 jen.Qual(PackageNameErrors, "MissingRequiredFieldError"). 506 ValuesFunc(func(group *jen.Group) { 507 group.Add(jen.Id("Name").Op(":").Lit(fieldName)) 508 }), 509 )) 510 })) 511 } 512 if arrayField := field.Type.Array; arrayField != nil { 513 if arrayField.Type.Struct != nil { 514 group.Add( 515 jen.For(jen.List(jen.Id("_"), jen.Id("value")).Op(":=").Range().Id("j").Dot(fieldName)). 516 BlockFunc(func(group *jen.Group) { 517 group.Add( 518 jen.If( 519 jen.Err().Op(":=").Id("value").Dot("validate").Call(), 520 jen.Err().Op("!=").Nil(), 521 ).BlockFunc(func(group *jen.Group) { 522 group.Return(jen.Err()) 523 }), 524 ) 525 }), 526 ) 527 } 528 } else if field.Type.Struct != nil { 529 group.Add( 530 jen.If( 531 jen.Err().Op(":=").Id("j").Dot(fieldName).Dot("validate").Call(), 532 jen.Err().Op("!=").Nil(), 533 ).BlockFunc(func(group *jen.Group) { 534 group.Return(jen.Err()) 535 }), 536 ) 537 } 538 } 539 } 540 group.Add(jen.Return(jen.Nil())) 541 })) 542 return nil 543 } 544 545 func (jsonStruct *JsonStruct) getServiceBucketField() *JsonField { 546 var serviceBucketField *JsonField = nil 547 548 for i := range jsonStruct.Fields { 549 if jsonStruct.Fields[i].ServiceBucket.ToServiceBucketType() != ServiceBucketTypeNone { 550 if serviceBucketField == nil { 551 serviceBucketField = &jsonStruct.Fields[i] 552 } else { 553 panic("multiple service bucket fields") 554 } 555 } 556 } 557 return serviceBucketField 558 } 559 560 func (jsonStruct *JsonStruct) getServiceObjectField() *JsonField { 561 var serviceObjectField *JsonField = nil 562 563 for i := range jsonStruct.Fields { 564 if jsonStruct.Fields[i].ServiceObject.ToServiceObjectType() != ServiceObjectTypeNone { 565 if serviceObjectField == nil { 566 serviceObjectField = &jsonStruct.Fields[i] 567 } else { 568 panic("multiple service object fields") 569 } 570 } 571 } 572 return serviceObjectField 573 } 574 575 func (jsonType *JsonType) UnmarshalYAML(value *yaml.Node) error { 576 switch value.ShortTag() { 577 case "!!str": 578 switch value.Value { 579 case "string": 580 jsonType.String = true 581 case "integer": 582 jsonType.Integer = true 583 case "float": 584 jsonType.Float = true 585 case "boolean": 586 jsonType.Boolean = true 587 case "any": 588 jsonType.Any = true 589 case "string_map": 590 jsonType.StringMap = true 591 default: 592 return fmt.Errorf("unknown json type: %s", value.Value) 593 } 594 return nil 595 case "!!map": 596 switch value.Content[0].Value { 597 case "array": 598 return value.Content[1].Decode(&jsonType.Array) 599 case "struct": 600 return value.Content[1].Decode(&jsonType.Struct) 601 default: 602 return fmt.Errorf("unknown json type: %s", value.Content[0].Value) 603 } 604 default: 605 return fmt.Errorf("unknown json type: %s", value.ShortTag()) 606 } 607 }