package tsgen
import (
"fmt"
"go/types"
"regexp"
"strings"
"github.com/projectdiscovery/utils/errkit"
)
func (p *EntityParser) scrapeAndCreate(typeName string) error {
if p.newObjects[typeName] == nil {
return nil
}
pkgName := strings.Split(typeName, ".")[0]
baseTypeName := strings.Split(typeName, ".")[1]
pkg, ok := p.imports[pkgName]
if !ok {
return errkit.Newf("package %v for type %v not found", pkgName, typeName)
}
obj := pkg.Types.Scope().Lookup(baseTypeName)
if obj == nil {
return errkit.Newf("type %v not found in package %+v", typeName, pkg)
}
typeNameObj, ok := obj.(*types.TypeName)
if !ok {
return errkit.Newf("%v is not a type name", typeName)
}
namedStruct, ok := typeNameObj.Type().Underlying().(*types.Struct)
if !ok {
return fmt.Errorf("%s is not a named struct type", typeName)
}
d := &ExtObject{
builtIn: make(map[string]string),
nested: map[string]map[string]*ExtObject{},
}
for i := 0; i < namedStruct.NumFields(); i++ {
field := namedStruct.Field(i)
fieldName := field.Name()
if field.Exported() {
recursiveScrapeType(nil, fieldName, field.Type(), d)
}
}
entityMap := make(map[string]Entity)
properties := ConvertExtObjectToEntities(d, entityMap)
entityMap[baseTypeName] = Entity{
Name: baseTypeName,
Type: "interface",
Description: fmt.Sprintf("%v Interface", baseTypeName),
Object: Interface{
Properties: properties,
},
}
for _, entity := range entityMap {
p.entities = append(p.entities, entity)
}
return nil
}
type ExtObject struct {
builtIn map[string]string
nested map[string]map[string]*ExtObject
}
func recursiveScrapeType(parentType types.Type, fieldName string, fieldType types.Type, extObject *ExtObject) {
if named, ok := fieldType.(*types.Named); ok && !named.Obj().Exported() {
return
}
if fieldType.String() == "time.Time" {
extObject.builtIn[fieldName] = "Date"
return
}
switch t := fieldType.Underlying().(type) {
case *types.Pointer:
recursiveScrapeType(nil, fieldName, t.Elem(), extObject)
case *types.Signature:
case *types.Basic:
if parentType != nil {
switch p := parentType.Underlying().(type) {
case *types.Slice:
extObject.builtIn[fieldName] = "[]" + fieldType.String()
case *types.Array:
extObject.builtIn[fieldName] = fmt.Sprintf("[%v]", p.Len()) + fieldType.String()
}
} else {
extObject.builtIn[fieldName] = fieldType.String()
}
case *types.Struct:
if extObject.nested[fieldName] == nil {
extObject.nested[fieldName] = make(map[string]*ExtObject)
}
nestedExtObject := &ExtObject{
builtIn: make(map[string]string),
nested: map[string]map[string]*ExtObject{},
}
extObject.nested[fieldName][fieldType.String()] = nestedExtObject
for i := 0; i < t.NumFields(); i++ {
field := t.Field(i)
if field.Exported() {
recursiveScrapeType(nil, field.Name(), field.Type(), nestedExtObject)
}
}
case *types.Array:
recursiveScrapeType(t, fieldName, t.Elem(), extObject)
case *types.Slice:
recursiveScrapeType(t, fieldName, t.Elem(), extObject)
default:
}
}
var re = regexp.MustCompile(`\[[0-9]+\].*`)
func ConvertExtObjectToEntities(extObj *ExtObject, nestedTypes map[string]Entity) []Property {
var properties []Property
for fieldName, fieldType := range extObj.builtIn {
var description string
if re.MatchString(fieldType) {
description = fmt.Sprintf("fixed size array of length: %v", fieldType[:strings.Index(fieldType, "]")+1])
fieldType = "[]" + fieldType[strings.Index(fieldType, "]")+1:]
}
if strings.Contains(fieldType, "time.Duration") {
description = "time in nanoseconds"
}
px := Property{
Name: fieldName,
Type: toTsTypes(fieldType),
Description: description,
}
if strings.HasPrefix(px.Type, "[") {
px.Type = fieldType[strings.Index(px.Type, "]")+1:] + "[]"
}
properties = append(properties, px)
}
for fieldName, nestedExtObjects := range extObj.nested {
for origType, nestedExtObject := range nestedExtObjects {
got := ConvertExtObjectToEntities(nestedExtObject, nestedTypes)
baseTypename := origType[strings.LastIndex(origType, ".")+1:]
nestedTypes[baseTypename] = Entity{
Name: baseTypename,
Description: fmt.Sprintf("%v Interface", baseTypename),
Type: "interface",
Object: Interface{
Properties: got,
},
}
properties = append(properties, Property{
Name: fieldName,
Type: baseTypename,
})
}
}
return properties
}