goscript 0.0.57 → 0.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -33
- package/compiler/analysis.go +115 -19
- package/compiler/assignment.go +163 -217
- package/compiler/compiler.go +35 -31
- package/compiler/composite-lit.go +233 -196
- package/compiler/constraint.go +88 -0
- package/compiler/decl.go +418 -7
- package/compiler/expr-call-async.go +20 -34
- package/compiler/expr-call-builtins.go +19 -0
- package/compiler/expr-call-helpers.go +0 -28
- package/compiler/expr-call-make.go +93 -343
- package/compiler/expr-call-type-conversion.go +221 -249
- package/compiler/expr-call.go +70 -69
- package/compiler/expr-selector.go +21 -24
- package/compiler/expr.go +3 -60
- package/compiler/protobuf.go +180 -36
- package/compiler/spec-value.go +132 -24
- package/compiler/spec.go +14 -55
- package/compiler/stmt-assign.go +338 -356
- package/compiler/stmt-range.go +4 -24
- package/compiler/stmt.go +99 -172
- package/compiler/type-utils.go +185 -0
- package/compiler/type.go +26 -80
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js +3 -0
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.js +8 -2
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/fmt/fmt.js +113 -16
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +1 -1
- package/dist/gs/runtime/runtime.js +1 -1
- package/dist/gs/slices/slices.d.ts +23 -0
- package/dist/gs/slices/slices.js +61 -0
- package/dist/gs/slices/slices.js.map +1 -1
- package/go.mod +8 -8
- package/go.sum +14 -14
- package/gs/builtin/slice.ts +5 -2
- package/gs/builtin/type.ts +13 -6
- package/gs/fmt/fmt.test.ts +176 -0
- package/gs/fmt/fmt.ts +109 -18
- package/gs/runtime/runtime.ts +1 -1
- package/gs/slices/slices.ts +68 -0
- package/package.json +3 -3
|
@@ -79,11 +79,7 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
79
79
|
// Check if it's a []byte literal
|
|
80
80
|
isByteSliceLiteral := false
|
|
81
81
|
if typInfo := c.pkg.TypesInfo.TypeOf(exp.Type); typInfo != nil {
|
|
82
|
-
|
|
83
|
-
if basicElem, ok := sliceT.Elem().(*types.Basic); ok && basicElem.Kind() == types.Uint8 {
|
|
84
|
-
isByteSliceLiteral = true
|
|
85
|
-
}
|
|
86
|
-
}
|
|
82
|
+
isByteSliceLiteral = c.isByteSliceType(typInfo)
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
if isByteSliceLiteral {
|
|
@@ -287,96 +283,12 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
287
283
|
|
|
288
284
|
if isStructLiteral && structType != nil {
|
|
289
285
|
// --- Struct Literal Handling (Nested) ---
|
|
290
|
-
|
|
291
|
-
embeddedFields :=
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
field := structType.Field(i)
|
|
297
|
-
if field.Anonymous() {
|
|
298
|
-
fieldType := field.Type()
|
|
299
|
-
if ptr, ok := fieldType.(*types.Pointer); ok {
|
|
300
|
-
fieldType = ptr.Elem()
|
|
301
|
-
}
|
|
302
|
-
if named, ok := fieldType.(*types.Named); ok {
|
|
303
|
-
// Use the type name as the property name in TS
|
|
304
|
-
embeddedPropName := named.Obj().Name()
|
|
305
|
-
embeddedFields[embeddedPropName] = make(map[string]ast.Expr)
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Group literal elements by direct vs embedded fields
|
|
311
|
-
for _, elt := range exp.Elts {
|
|
312
|
-
kv, ok := elt.(*ast.KeyValueExpr)
|
|
313
|
-
if !ok {
|
|
314
|
-
continue
|
|
315
|
-
} // Skip non-key-value
|
|
316
|
-
keyIdent, ok := kv.Key.(*ast.Ident)
|
|
317
|
-
if !ok {
|
|
318
|
-
continue
|
|
319
|
-
} // Skip non-ident keys
|
|
320
|
-
keyName := keyIdent.Name
|
|
321
|
-
|
|
322
|
-
// Check if this is an explicit embedded struct initialization
|
|
323
|
-
// e.g., Person: Person{...} or Person: personVar
|
|
324
|
-
if _, isEmbedded := embeddedFields[keyName]; isEmbedded {
|
|
325
|
-
// This is an explicit initialization of an embedded struct
|
|
326
|
-
explicitEmbedded[keyName] = kv.Value
|
|
327
|
-
continue
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
isDirectField := false
|
|
331
|
-
for i := range structType.NumFields() {
|
|
332
|
-
field := structType.Field(i)
|
|
333
|
-
if field.Name() == keyName {
|
|
334
|
-
isDirectField = true
|
|
335
|
-
directFields[keyName] = kv.Value
|
|
336
|
-
break
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// For anonymous structs, all fields are direct fields
|
|
341
|
-
if isAnonymousStruct {
|
|
342
|
-
directFields[keyName] = kv.Value
|
|
343
|
-
isDirectField = true
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// If not a direct field, return an error
|
|
347
|
-
if !isDirectField {
|
|
348
|
-
// This field was not found as a direct field in the struct
|
|
349
|
-
return fmt.Errorf("field %s not found in type %s for composite literal",
|
|
350
|
-
keyName, litType.String())
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// Handle the case where an anonymous struct has values without keys
|
|
355
|
-
// This block processes non-key-value elements and associates them with struct fields.
|
|
356
|
-
if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 {
|
|
357
|
-
// Check if any elements in the composite literal are not key-value pairs.
|
|
358
|
-
hasNonKeyValueElts := false
|
|
359
|
-
for _, elt := range exp.Elts {
|
|
360
|
-
// If an element is not a key-value pair, set the flag to true.
|
|
361
|
-
if _, isKV := elt.(*ast.KeyValueExpr); !isKV {
|
|
362
|
-
hasNonKeyValueElts = true
|
|
363
|
-
break
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if hasNonKeyValueElts {
|
|
368
|
-
// Get the fields from the struct type
|
|
369
|
-
for i := 0; i < structType.NumFields(); i++ {
|
|
370
|
-
field := structType.Field(i)
|
|
371
|
-
// If we have a value for this field position
|
|
372
|
-
if i < len(exp.Elts) {
|
|
373
|
-
// Check if it's not a key-value pair
|
|
374
|
-
if _, isKV := exp.Elts[i].(*ast.KeyValueExpr); !isKV {
|
|
375
|
-
directFields[field.Name()] = exp.Elts[i]
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
286
|
+
// Categorize fields into direct, embedded, and explicit embedded
|
|
287
|
+
directFields, embeddedFields, explicitEmbedded, err := c.categorizeStructFields(
|
|
288
|
+
exp, structType, litType, isAnonymousStruct,
|
|
289
|
+
)
|
|
290
|
+
if err != nil {
|
|
291
|
+
return err
|
|
380
292
|
}
|
|
381
293
|
|
|
382
294
|
// Write the object literal
|
|
@@ -388,107 +300,9 @@ func (c *GoToTSCompiler) WriteCompositeLit(exp *ast.CompositeLit) error {
|
|
|
388
300
|
c.tsw.WriteLiterally("({")
|
|
389
301
|
}
|
|
390
302
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
directKeys := make([]string, 0, len(directFields))
|
|
395
|
-
for k := range directFields {
|
|
396
|
-
// Skip embedded struct names - we'll handle those separately
|
|
397
|
-
if _, isEmbedded := embeddedFields[k]; !isEmbedded {
|
|
398
|
-
directKeys = append(directKeys, k)
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
slices.Sort(directKeys)
|
|
402
|
-
for _, keyName := range directKeys {
|
|
403
|
-
if firstFieldWritten {
|
|
404
|
-
c.tsw.WriteLiterally(", ")
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Convert field name for protobuf types
|
|
408
|
-
fieldName := c.convertProtobufFieldNameInLiteral(keyName, litType)
|
|
409
|
-
|
|
410
|
-
c.tsw.WriteLiterally(fieldName)
|
|
411
|
-
c.tsw.WriteLiterally(": ")
|
|
412
|
-
if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
|
|
413
|
-
return err
|
|
414
|
-
}
|
|
415
|
-
firstFieldWritten = true
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Write explicitly initialized embedded structs
|
|
419
|
-
explicitKeys := make([]string, 0, len(explicitEmbedded))
|
|
420
|
-
for k := range explicitEmbedded {
|
|
421
|
-
explicitKeys = append(explicitKeys, k)
|
|
422
|
-
}
|
|
423
|
-
slices.Sort(explicitKeys)
|
|
424
|
-
for _, embeddedName := range explicitKeys {
|
|
425
|
-
if firstFieldWritten {
|
|
426
|
-
c.tsw.WriteLiterally(", ")
|
|
427
|
-
}
|
|
428
|
-
c.tsw.WriteLiterally(embeddedName)
|
|
429
|
-
c.tsw.WriteLiterally(": ")
|
|
430
|
-
|
|
431
|
-
// Check if the embedded value is a composite literal for a struct
|
|
432
|
-
// If so, extract the fields and write them directly
|
|
433
|
-
if compLit, ok := explicitEmbedded[embeddedName].(*ast.CompositeLit); ok {
|
|
434
|
-
// Write initialization fields directly without the 'new Constructor'
|
|
435
|
-
c.tsw.WriteLiterally("{")
|
|
436
|
-
for i, elem := range compLit.Elts {
|
|
437
|
-
if i > 0 {
|
|
438
|
-
c.tsw.WriteLiterally(", ")
|
|
439
|
-
}
|
|
440
|
-
if err := c.WriteVarRefedValue(elem); err != nil {
|
|
441
|
-
return err
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
c.tsw.WriteLiterally("}")
|
|
445
|
-
} else {
|
|
446
|
-
// Not a composite literal, write it normally
|
|
447
|
-
if err := c.WriteVarRefedValue(explicitEmbedded[embeddedName]); err != nil {
|
|
448
|
-
return err
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
firstFieldWritten = true
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Write embedded fields for structs that weren't explicitly initialized
|
|
455
|
-
embeddedKeys := make([]string, 0, len(embeddedFields))
|
|
456
|
-
for k := range embeddedFields {
|
|
457
|
-
// Skip embedded structs that were explicitly initialized
|
|
458
|
-
if _, wasExplicit := explicitEmbedded[k]; !wasExplicit {
|
|
459
|
-
embeddedKeys = append(embeddedKeys, k)
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
slices.Sort(embeddedKeys)
|
|
463
|
-
for _, embeddedPropName := range embeddedKeys {
|
|
464
|
-
fieldsMap := embeddedFields[embeddedPropName]
|
|
465
|
-
if len(fieldsMap) == 0 {
|
|
466
|
-
continue
|
|
467
|
-
} // Skip empty embedded initializers
|
|
468
|
-
|
|
469
|
-
if firstFieldWritten {
|
|
470
|
-
c.tsw.WriteLiterally(", ")
|
|
471
|
-
}
|
|
472
|
-
c.tsw.WriteLiterally(embeddedPropName) // Use the Type name as the property key
|
|
473
|
-
c.tsw.WriteLiterally(": {")
|
|
474
|
-
|
|
475
|
-
innerKeys := make([]string, 0, len(fieldsMap))
|
|
476
|
-
for k := range fieldsMap {
|
|
477
|
-
innerKeys = append(innerKeys, k)
|
|
478
|
-
}
|
|
479
|
-
slices.Sort(innerKeys)
|
|
480
|
-
for i, keyName := range innerKeys {
|
|
481
|
-
if i > 0 {
|
|
482
|
-
c.tsw.WriteLiterally(", ")
|
|
483
|
-
}
|
|
484
|
-
c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
|
|
485
|
-
c.tsw.WriteLiterally(": ")
|
|
486
|
-
if err := c.WriteVarRefedValue(fieldsMap[keyName]); err != nil {
|
|
487
|
-
return err
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
c.tsw.WriteLiterally("}")
|
|
491
|
-
firstFieldWritten = true
|
|
303
|
+
// Write all fields
|
|
304
|
+
if err := c.writeStructLiteralFields(directFields, embeddedFields, explicitEmbedded, litType); err != nil {
|
|
305
|
+
return err
|
|
492
306
|
}
|
|
493
307
|
|
|
494
308
|
// Close the object literal
|
|
@@ -714,3 +528,226 @@ func (c *GoToTSCompiler) evaluateConstantExpr(expr ast.Expr) interface{} {
|
|
|
714
528
|
}
|
|
715
529
|
return nil
|
|
716
530
|
}
|
|
531
|
+
|
|
532
|
+
// categorizeStructFields organizes the elements of a struct composite literal into
|
|
533
|
+
// three categories: direct fields, embedded fields, and explicitly initialized embedded structs.
|
|
534
|
+
// Returns maps for direct fields, embedded fields (nested map), and explicit embedded initializations.
|
|
535
|
+
func (c *GoToTSCompiler) categorizeStructFields(
|
|
536
|
+
exp *ast.CompositeLit,
|
|
537
|
+
structType *types.Struct,
|
|
538
|
+
litType types.Type,
|
|
539
|
+
isAnonymousStruct bool,
|
|
540
|
+
) (
|
|
541
|
+
directFields map[string]ast.Expr,
|
|
542
|
+
embeddedFields map[string]map[string]ast.Expr,
|
|
543
|
+
explicitEmbedded map[string]ast.Expr,
|
|
544
|
+
err error,
|
|
545
|
+
) {
|
|
546
|
+
directFields = make(map[string]ast.Expr)
|
|
547
|
+
embeddedFields = make(map[string]map[string]ast.Expr)
|
|
548
|
+
explicitEmbedded = make(map[string]ast.Expr)
|
|
549
|
+
|
|
550
|
+
// Pre-populate embeddedFields map keys using the correct property name
|
|
551
|
+
for i := 0; i < structType.NumFields(); i++ {
|
|
552
|
+
field := structType.Field(i)
|
|
553
|
+
if field.Anonymous() {
|
|
554
|
+
fieldType := field.Type()
|
|
555
|
+
if ptr, ok := fieldType.(*types.Pointer); ok {
|
|
556
|
+
fieldType = ptr.Elem()
|
|
557
|
+
}
|
|
558
|
+
if named, ok := fieldType.(*types.Named); ok {
|
|
559
|
+
// Use the type name as the property name in TS
|
|
560
|
+
embeddedPropName := named.Obj().Name()
|
|
561
|
+
embeddedFields[embeddedPropName] = make(map[string]ast.Expr)
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Group literal elements by direct vs embedded fields
|
|
567
|
+
for _, elt := range exp.Elts {
|
|
568
|
+
kv, ok := elt.(*ast.KeyValueExpr)
|
|
569
|
+
if !ok {
|
|
570
|
+
continue
|
|
571
|
+
} // Skip non-key-value
|
|
572
|
+
keyIdent, ok := kv.Key.(*ast.Ident)
|
|
573
|
+
if !ok {
|
|
574
|
+
continue
|
|
575
|
+
} // Skip non-ident keys
|
|
576
|
+
keyName := keyIdent.Name
|
|
577
|
+
|
|
578
|
+
// Check if this is an explicit embedded struct initialization
|
|
579
|
+
// e.g., Person: Person{...} or Person: personVar
|
|
580
|
+
if _, isEmbedded := embeddedFields[keyName]; isEmbedded {
|
|
581
|
+
// This is an explicit initialization of an embedded struct
|
|
582
|
+
explicitEmbedded[keyName] = kv.Value
|
|
583
|
+
continue
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
isDirectField := false
|
|
587
|
+
for i := range structType.NumFields() {
|
|
588
|
+
field := structType.Field(i)
|
|
589
|
+
if field.Name() == keyName {
|
|
590
|
+
isDirectField = true
|
|
591
|
+
directFields[keyName] = kv.Value
|
|
592
|
+
break
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// For anonymous structs, all fields are direct fields
|
|
597
|
+
if isAnonymousStruct {
|
|
598
|
+
directFields[keyName] = kv.Value
|
|
599
|
+
isDirectField = true
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// If not a direct field, return an error
|
|
603
|
+
if !isDirectField {
|
|
604
|
+
return nil, nil, nil, fmt.Errorf("field %s not found in type %s for composite literal",
|
|
605
|
+
keyName, litType.String())
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Handle the case where an anonymous struct has values without keys
|
|
610
|
+
// This block processes non-key-value elements and associates them with struct fields.
|
|
611
|
+
if isAnonymousStruct && len(exp.Elts) > 0 && len(directFields) == 0 {
|
|
612
|
+
// Check if any elements in the composite literal are not key-value pairs.
|
|
613
|
+
hasNonKeyValueElts := false
|
|
614
|
+
for _, elt := range exp.Elts {
|
|
615
|
+
// If an element is not a key-value pair, set the flag to true.
|
|
616
|
+
if _, isKV := elt.(*ast.KeyValueExpr); !isKV {
|
|
617
|
+
hasNonKeyValueElts = true
|
|
618
|
+
break
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if hasNonKeyValueElts {
|
|
623
|
+
// Get the fields from the struct type
|
|
624
|
+
for i := 0; i < structType.NumFields(); i++ {
|
|
625
|
+
field := structType.Field(i)
|
|
626
|
+
// If we have a value for this field position
|
|
627
|
+
if i < len(exp.Elts) {
|
|
628
|
+
// Check if it's not a key-value pair
|
|
629
|
+
if _, isKV := exp.Elts[i].(*ast.KeyValueExpr); !isKV {
|
|
630
|
+
directFields[field.Name()] = exp.Elts[i]
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return directFields, embeddedFields, explicitEmbedded, nil
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// writeStructLiteralFields writes the field initializations for a struct composite literal.
|
|
641
|
+
// It handles direct fields, explicitly initialized embedded structs, and implicitly initialized
|
|
642
|
+
// embedded fields, writing them in sorted order.
|
|
643
|
+
func (c *GoToTSCompiler) writeStructLiteralFields(
|
|
644
|
+
directFields map[string]ast.Expr,
|
|
645
|
+
embeddedFields map[string]map[string]ast.Expr,
|
|
646
|
+
explicitEmbedded map[string]ast.Expr,
|
|
647
|
+
litType types.Type,
|
|
648
|
+
) error {
|
|
649
|
+
firstFieldWritten := false
|
|
650
|
+
|
|
651
|
+
// Write direct fields that aren't embedded struct names
|
|
652
|
+
directKeys := make([]string, 0, len(directFields))
|
|
653
|
+
for k := range directFields {
|
|
654
|
+
// Skip embedded struct names - we'll handle those separately
|
|
655
|
+
if _, isEmbedded := embeddedFields[k]; !isEmbedded {
|
|
656
|
+
directKeys = append(directKeys, k)
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
slices.Sort(directKeys)
|
|
660
|
+
for _, keyName := range directKeys {
|
|
661
|
+
if firstFieldWritten {
|
|
662
|
+
c.tsw.WriteLiterally(", ")
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Convert field name for protobuf types
|
|
666
|
+
fieldName := c.convertProtobufFieldNameInLiteral(keyName, litType)
|
|
667
|
+
|
|
668
|
+
c.tsw.WriteLiterally(fieldName)
|
|
669
|
+
c.tsw.WriteLiterally(": ")
|
|
670
|
+
if err := c.WriteVarRefedValue(directFields[keyName]); err != nil {
|
|
671
|
+
return err
|
|
672
|
+
}
|
|
673
|
+
firstFieldWritten = true
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Write explicitly initialized embedded structs
|
|
677
|
+
explicitKeys := make([]string, 0, len(explicitEmbedded))
|
|
678
|
+
for k := range explicitEmbedded {
|
|
679
|
+
explicitKeys = append(explicitKeys, k)
|
|
680
|
+
}
|
|
681
|
+
slices.Sort(explicitKeys)
|
|
682
|
+
for _, embeddedName := range explicitKeys {
|
|
683
|
+
if firstFieldWritten {
|
|
684
|
+
c.tsw.WriteLiterally(", ")
|
|
685
|
+
}
|
|
686
|
+
c.tsw.WriteLiterally(embeddedName)
|
|
687
|
+
c.tsw.WriteLiterally(": ")
|
|
688
|
+
|
|
689
|
+
// Check if the embedded value is a composite literal for a struct
|
|
690
|
+
// If so, extract the fields and write them directly
|
|
691
|
+
if compLit, ok := explicitEmbedded[embeddedName].(*ast.CompositeLit); ok {
|
|
692
|
+
// Write initialization fields directly without the 'new Constructor'
|
|
693
|
+
c.tsw.WriteLiterally("{")
|
|
694
|
+
for i, elem := range compLit.Elts {
|
|
695
|
+
if i > 0 {
|
|
696
|
+
c.tsw.WriteLiterally(", ")
|
|
697
|
+
}
|
|
698
|
+
if err := c.WriteVarRefedValue(elem); err != nil {
|
|
699
|
+
return err
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
c.tsw.WriteLiterally("}")
|
|
703
|
+
} else {
|
|
704
|
+
// Not a composite literal, write it normally
|
|
705
|
+
if err := c.WriteVarRefedValue(explicitEmbedded[embeddedName]); err != nil {
|
|
706
|
+
return err
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
firstFieldWritten = true
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// Write embedded fields for structs that weren't explicitly initialized
|
|
713
|
+
embeddedKeys := make([]string, 0, len(embeddedFields))
|
|
714
|
+
for k := range embeddedFields {
|
|
715
|
+
// Skip embedded structs that were explicitly initialized
|
|
716
|
+
if _, wasExplicit := explicitEmbedded[k]; !wasExplicit {
|
|
717
|
+
embeddedKeys = append(embeddedKeys, k)
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
slices.Sort(embeddedKeys)
|
|
721
|
+
for _, embeddedPropName := range embeddedKeys {
|
|
722
|
+
fieldsMap := embeddedFields[embeddedPropName]
|
|
723
|
+
if len(fieldsMap) == 0 {
|
|
724
|
+
continue
|
|
725
|
+
} // Skip empty embedded initializers
|
|
726
|
+
|
|
727
|
+
if firstFieldWritten {
|
|
728
|
+
c.tsw.WriteLiterally(", ")
|
|
729
|
+
}
|
|
730
|
+
c.tsw.WriteLiterally(embeddedPropName) // Use the Type name as the property key
|
|
731
|
+
c.tsw.WriteLiterally(": {")
|
|
732
|
+
|
|
733
|
+
innerKeys := make([]string, 0, len(fieldsMap))
|
|
734
|
+
for k := range fieldsMap {
|
|
735
|
+
innerKeys = append(innerKeys, k)
|
|
736
|
+
}
|
|
737
|
+
slices.Sort(innerKeys)
|
|
738
|
+
for i, keyName := range innerKeys {
|
|
739
|
+
if i > 0 {
|
|
740
|
+
c.tsw.WriteLiterally(", ")
|
|
741
|
+
}
|
|
742
|
+
c.tsw.WriteLiterally(keyName) // Field name within the embedded struct
|
|
743
|
+
c.tsw.WriteLiterally(": ")
|
|
744
|
+
if err := c.WriteVarRefedValue(fieldsMap[keyName]); err != nil {
|
|
745
|
+
return err
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
c.tsw.WriteLiterally("}")
|
|
749
|
+
firstFieldWritten = true
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
return nil
|
|
753
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import "go/types"
|
|
4
|
+
|
|
5
|
+
// ConstraintInfo holds information about types found in an interface constraint
|
|
6
|
+
type ConstraintInfo struct {
|
|
7
|
+
HasMap bool
|
|
8
|
+
HasSlice bool
|
|
9
|
+
HasString bool
|
|
10
|
+
HasByteSlice bool
|
|
11
|
+
MapValueType types.Type
|
|
12
|
+
SliceElemType types.Type
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// analyzeConstraint analyzes an interface constraint and returns information about the types it contains
|
|
16
|
+
func analyzeConstraint(iface *types.Interface) ConstraintInfo {
|
|
17
|
+
info := ConstraintInfo{}
|
|
18
|
+
|
|
19
|
+
for i := 0; i < iface.NumEmbeddeds(); i++ {
|
|
20
|
+
embedded := iface.EmbeddedType(i)
|
|
21
|
+
if union, ok := embedded.(*types.Union); ok {
|
|
22
|
+
for j := 0; j < union.Len(); j++ {
|
|
23
|
+
term := union.Term(j)
|
|
24
|
+
checkType(term.Type(), &info)
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
checkType(embedded, &info)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return info
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
func checkType(t types.Type, info *ConstraintInfo) {
|
|
35
|
+
underlying := t.Underlying()
|
|
36
|
+
|
|
37
|
+
if mapType, isMap := underlying.(*types.Map); isMap {
|
|
38
|
+
info.HasMap = true
|
|
39
|
+
if info.MapValueType == nil {
|
|
40
|
+
info.MapValueType = mapType.Elem()
|
|
41
|
+
}
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if sliceType, isSlice := underlying.(*types.Slice); isSlice {
|
|
46
|
+
info.HasSlice = true
|
|
47
|
+
if info.SliceElemType == nil {
|
|
48
|
+
info.SliceElemType = sliceType.Elem()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if elemType, isBasic := sliceType.Elem().(*types.Basic); isBasic && elemType.Kind() == types.Uint8 {
|
|
52
|
+
info.HasByteSlice = true
|
|
53
|
+
}
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if basicType, isBasic := underlying.(*types.Basic); isBasic {
|
|
58
|
+
if (basicType.Info() & types.IsString) != 0 {
|
|
59
|
+
info.HasString = true
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// hasMapConstraint checks if an interface constraint includes map types
|
|
65
|
+
func hasMapConstraint(iface *types.Interface) bool {
|
|
66
|
+
return analyzeConstraint(iface).HasMap
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// hasSliceConstraint checks if an interface constraint includes slice types
|
|
70
|
+
func hasSliceConstraint(iface *types.Interface) bool {
|
|
71
|
+
return analyzeConstraint(iface).HasSlice
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// hasMixedStringByteConstraint checks if an interface constraint includes both string and []byte types
|
|
75
|
+
func hasMixedStringByteConstraint(iface *types.Interface) bool {
|
|
76
|
+
info := analyzeConstraint(iface)
|
|
77
|
+
return info.HasString && info.HasByteSlice
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// getMapValueTypeFromConstraint extracts the value type from a map constraint
|
|
81
|
+
func getMapValueTypeFromConstraint(iface *types.Interface) types.Type {
|
|
82
|
+
return analyzeConstraint(iface).MapValueType
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// getSliceElementTypeFromConstraint extracts the element type from a slice constraint
|
|
86
|
+
func getSliceElementTypeFromConstraint(iface *types.Interface) types.Type {
|
|
87
|
+
return analyzeConstraint(iface).SliceElemType
|
|
88
|
+
}
|