mirror of
https://github.com/opbnq-q/nto-cli.git
synced 2025-12-06 18:40:34 +07:00
feat: new parser & generator
This commit is contained in:
151
model/parse_models.go
Normal file
151
model/parse_models.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log/slog"
|
||||
"nto_cli/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/kuzgoga/fogg"
|
||||
)
|
||||
|
||||
func ParseModelsPackage(modelsPkgDir string) ([]Model, error) {
|
||||
var models []Model
|
||||
fileset := token.NewFileSet()
|
||||
|
||||
files, err := os.ReadDir(modelsPkgDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
filepathPath := filepath.Join(modelsPkgDir, file.Name())
|
||||
fileAst, err := parser.ParseFile(fileset, filepathPath, nil, parser.DeclarationErrors)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
models = append(models, parseFileStructs(fileset, fileAst.Decls)...)
|
||||
}
|
||||
|
||||
ParseRelatedModels(&models)
|
||||
|
||||
return models, nil
|
||||
}
|
||||
|
||||
func parseFileStructs(fileSet *token.FileSet, decls []ast.Decl) []Model {
|
||||
var models []Model
|
||||
for _, decl := range decls {
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if genDecl.Tok != token.TYPE {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, spec := range genDecl.Specs {
|
||||
typeSpec, ok := spec.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := typeSpec.Type.(*ast.StructType); ok && typeSpec.Name != nil {
|
||||
models = append(models, parseModelDecl(fileSet, typeSpec))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return models
|
||||
}
|
||||
|
||||
func parseModelDecl(fileset *token.FileSet, decl *ast.TypeSpec) Model {
|
||||
name := decl.Name.Name
|
||||
structType, _ := decl.Type.(*ast.StructType)
|
||||
var fields []Field
|
||||
|
||||
for _, fieldDecl := range structType.Fields.List {
|
||||
fieldPos := fileset.Position(fieldDecl.Pos()).String()
|
||||
if len(fieldDecl.Names) == 0 {
|
||||
slog.Error("%s Embedded structure isn't supported", fieldPos)
|
||||
continue
|
||||
}
|
||||
|
||||
var tag string
|
||||
if fieldDecl.Tag != nil {
|
||||
tag = fieldDecl.Tag.Value[1 : len(fieldDecl.Tag.Value)-1]
|
||||
}
|
||||
|
||||
storage, err := fogg.Parse(tag)
|
||||
if err != nil {
|
||||
slog.Error("%s Struct tag parsing error: %s", fieldPos, err)
|
||||
}
|
||||
|
||||
var metadata FieldMetadata
|
||||
|
||||
if storage.HasTag("ui") {
|
||||
uiTag := storage.GetTag("ui")
|
||||
metadata.Hidden = uiTag.HasOption("hidden")
|
||||
metadata.Label = uiTag.GetParamOr("label", "")
|
||||
metadata.IsSlice = utils.IsSlice(fieldDecl.Type)
|
||||
metadata.IsPrimitiveType = utils.IsPrimitiveType(fieldDecl.Type)
|
||||
metadata.Datatype = uiTag.GetParamOr("datatype", "")
|
||||
if uiTag.HasParam("field") {
|
||||
metadata.RelatedFields = strings.Split(uiTag.GetParam("field").Value, ".")
|
||||
}
|
||||
} else {
|
||||
slog.Warn("%s Field does not have a UI tag", fieldPos)
|
||||
}
|
||||
|
||||
field := Field{
|
||||
Name: fieldDecl.Names[0].Name,
|
||||
Type: fieldDecl.Type,
|
||||
Tag: tag,
|
||||
Metadata: metadata,
|
||||
}
|
||||
fields = append(fields, field)
|
||||
}
|
||||
return Model{
|
||||
Name: name,
|
||||
Fields: fields,
|
||||
}
|
||||
}
|
||||
|
||||
func ParseRelatedModels(models *[]Model) {
|
||||
for i := range *models {
|
||||
model := &(*models)[i]
|
||||
for j := range model.Fields {
|
||||
field := &model.Fields[j]
|
||||
if field.Metadata.IsPrimitiveType {
|
||||
continue
|
||||
}
|
||||
|
||||
relatedModelName := utils.ResolveBaseType(field.Type)
|
||||
if relatedModelName == nil {
|
||||
slog.Error("Failed to resolve base type for field `%s` in model `%s`", field.Name, model.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, m := range *models {
|
||||
if m.Name == *relatedModelName {
|
||||
field.Metadata.RelatedModel = m.Name
|
||||
field.Metadata.IsRelatedModel = true
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
slog.Error("Cannot classify field type `%s` in model `%s`, type `%s`",
|
||||
field.Name, model.Name, *relatedModelName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user