diff --git a/cmd/input_dialog.go b/cmd/input_dialog.go index fae871e..dd53219 100644 --- a/cmd/input_dialog.go +++ b/cmd/input_dialog.go @@ -2,35 +2,28 @@ package cmd import ( "log" - "nto_cli/utils" + "nto_cli/model" "os" "github.com/rivo/tview" ) -func SelectionInput() ([]string, string) { - if len(os.Args) == 1 { - log.Fatalf("Please provide path to models.go") - } +func SelectionInput(models []model.Model) *[]model.Model { + unimplementedModels := model.GetNotImplementedModels(models) + var result []model.Model - modelsPath := os.Args[1] - - structNames := utils.GetNotImplementedStructs(modelsPath) - - if len(structNames) == 0 { + if len(unimplementedModels) == 0 { log.Println("No unimplemented models -> nothing to do") os.Exit(0) } - var result []string - app := tview.NewApplication() form := tview.NewForm() var checkboxes []*tview.Checkbox - for _, name := range structNames { - cb := tview.NewCheckbox().SetLabel(name) + for _, m := range unimplementedModels { + cb := tview.NewCheckbox().SetLabel(m.Name) checkboxes = append(checkboxes, cb) form.AddFormItem(cb) } @@ -38,15 +31,15 @@ func SelectionInput() ([]string, string) { form.AddButton("Generate", func() { for i, cb := range checkboxes { if cb.IsChecked() { - result = append(result, structNames[i]) + result = append(result, unimplementedModels[i]) } } app.Stop() }) if err := app.SetRoot(form, true).Run(); err != nil { - panic(err) + log.Fatalf("Failed to initialize dialog: %s", err) } - return result, modelsPath + return &result } diff --git a/entities/field.go b/entities/field.go deleted file mode 100644 index e2dbeb9..0000000 --- a/entities/field.go +++ /dev/null @@ -1,7 +0,0 @@ -package entities - -type Field struct { - Name string - Type string - Metadata []Metadata -} diff --git a/entities/metadata.go b/entities/metadata.go deleted file mode 100644 index 3e1e871..0000000 --- a/entities/metadata.go +++ /dev/null @@ -1,33 +0,0 @@ -package entities - -import ( - "strings" -) - -type Metadata struct { - Name string - Values []string -} - -func NewMetadata(tag string) *Metadata { - tag = strings.TrimSpace(tag) - tagName := "" - var values []string - if strings.Contains(tag, "=") { - tagName = tag[:strings.Index(tag, "=")] - if tag[strings.Index(tag, "=")+1] == '[' { - values = append(values, strings.Split(tag[strings.Index(tag, "=")+2:len(tag)-1], ";")...) - for i := range values { - values[i] = strings.TrimSpace(values[i]) - } - } else { - values = append(values, strings.TrimSpace(tag[strings.Index(tag, "=")+1:])) - } - } else { - tagName = tag - } - return &Metadata{ - Name: strings.TrimSpace(strings.ToLower(tagName)), - Values: values, - } -} diff --git a/generation/gen.go b/generation/gen.go index 927aead..5960be4 100644 --- a/generation/gen.go +++ b/generation/gen.go @@ -2,18 +2,18 @@ package generation import ( "log" - "nto_cli/entities" + "nto_cli/model" "nto_cli/utils" "os" "path/filepath" "strings" ) -func Generate(structName string, fields []entities.Field) { - mkPath := filepath.Join(utils.FindFrontendPath(), strings.ToLower(structName)) +func Generate(model *model.Model) { + mkPath := filepath.Join(utils.FindFrontendPath(), strings.ToLower(model.Name)) if err := os.Mkdir(mkPath, 0755); err != nil { log.Fatalf("Failed to mkdir for model: %s", err) } - GenerateService(structName, mkPath) - GenerateScheme(structName, fields, mkPath) + GenerateService(model, mkPath) + GenerateScheme(model, mkPath) } diff --git a/generation/scheme.go b/generation/scheme.go index 9f195d1..38bbf19 100644 --- a/generation/scheme.go +++ b/generation/scheme.go @@ -4,7 +4,7 @@ import ( _ "embed" "fmt" "log" - "nto_cli/entities" + "nto_cli/model" "nto_cli/utils" "os" "path/filepath" @@ -28,20 +28,20 @@ type TemplateData struct { StructName string LowerName string GolangServicesPath string - Fields []entities.Field + Fields []model.Field Dependencies []Dependency } -func GenerateScheme(structName string, fields []entities.Field, mkPath string) { +func GenerateScheme(model *model.Model, mkPath string) { data := TemplateData{ - StructName: structName, - LowerName: strings.ToLower(structName), + StructName: model.Name, + LowerName: strings.ToLower(model.Name), GolangServicesPath: GolangServicesPath, - Fields: fields, - Dependencies: processDependencies(fields), + Fields: model.Fields, + Dependencies: processDependencies(model.Fields), } - schemeFilename := strings.ToUpper(structName[:1]) + strings.ToLower(structName[1:]) + "Scheme.vue" + schemeFilename := strings.ToUpper(model.Name[:1]) + strings.ToLower(model.Name[1:]) + "Scheme.vue" schemeFilePath := filepath.Join(mkPath, schemeFilename) schemeFile, err := os.Create(schemeFilePath) if err != nil { @@ -63,25 +63,25 @@ func GenerateScheme(structName string, fields []entities.Field, mkPath string) { if err != nil { log.Fatalf("Failed to execute template: %s", err) } - log.Printf("Scheme for `%s` model is written: %s", structName, schemeFilePath) + log.Printf("Scheme for `%s` model is written: %s", model.Name, schemeFilePath) _ = utils.FormatFilesWithPrettier([]string{schemeFilePath}) } -func processDependencies(fields []entities.Field) []Dependency { +func processDependencies(fields []model.Field) []Dependency { var dependencies []Dependency for _, field := range fields { - for _, meta := range field.Metadata { - if meta.Name == "data" { - dependency := meta.Values[0] - dependencies = append(dependencies, Dependency{ - ImportName: strings.ToUpper(dependency[:1]) + strings.ToLower(dependency[1:]) + "Service", - ServiceName: strings.ToLower(dependency) + "Service", - LowerName: strings.ToLower(dependency), - FieldName: field.Name, - }) - } + dependency := field.Metadata.RelatedModel + if dependency == "" { + continue } + dependencies = append(dependencies, Dependency{ + ImportName: strings.ToUpper(dependency[:1]) + strings.ToLower(dependency[1:]) + "Service", + ServiceName: strings.ToLower(dependency) + "Service", + LowerName: strings.ToLower(dependency), + FieldName: field.Name, + }) + } return dependencies diff --git a/generation/service.go b/generation/service.go index 6a40ec9..38d75b0 100644 --- a/generation/service.go +++ b/generation/service.go @@ -4,6 +4,7 @@ import ( _ "embed" "fmt" "log" + "nto_cli/model" "nto_cli/utils" "os" "path/filepath" @@ -20,8 +21,8 @@ type ServiceTemplateContext struct { ServicesPath string } -func GenerateService(structName, mkPath string) { - servicePath := filepath.Join(mkPath, strings.ToLower(structName)+".service.ts") +func GenerateService(model *model.Model, mkPath string) { + servicePath := filepath.Join(mkPath, strings.ToLower(model.Name)+".service.ts") serviceFile, err := os.Create(servicePath) if err != nil { log.Fatalf("Failed to create service file: %s", err) @@ -35,8 +36,8 @@ func GenerateService(structName, mkPath string) { }(serviceFile) context := ServiceTemplateContext{ - LowerModelName: strings.ToLower(structName), - ModelName: structName, + LowerModelName: strings.ToLower(model.Name), + ModelName: model.Name, ServicesPath: GolangServicesPath, } @@ -50,6 +51,6 @@ func GenerateService(structName, mkPath string) { if err != nil { log.Fatalf("Failed to execute template: %s", err) } - log.Printf("Service for `%s` model is written: %s", structName, servicePath) + log.Printf("Service for `%s` model is written: %s", model.Name, servicePath) _ = utils.FormatFilesWithPrettier([]string{servicePath}) } diff --git a/generation/templates/scheme.tmpl b/generation/templates/scheme.tmpl index 2639788..5ce89c6 100644 --- a/generation/templates/scheme.tmpl +++ b/generation/templates/scheme.tmpl @@ -20,7 +20,7 @@ onMounted(async () => { const scheme: Scheme<{{.StructName}}> = reactive({ {{range .Fields}} - {{.Name}}: {{.Generate}}, + {{.Name}}: {{.GenerateFieldCode}}, {{end}} }) @@ -32,4 +32,4 @@ const getDefaults = () => getDefaultValues(scheme)
- \ No newline at end of file + diff --git a/go.mod b/go.mod index f6d14fb..9fe2d31 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/fatih/structtag v1.2.0 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/gdamore/tcell/v2 v2.7.1 // indirect + github.com/kuzgoga/fogg v0.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/rivo/tview v0.0.0-20241227133733-17b7edb88c57 // indirect diff --git a/go.sum b/go.sum index bc22373..dead313 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdk github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/tcell/v2 v2.7.1 h1:TiCcmpWHiAU7F0rA2I3S2Y4mmLmO9KHxJ7E1QhYzQbc= github.com/gdamore/tcell/v2 v2.7.1/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= +github.com/kuzgoga/fogg v0.1.0 h1:W4EqVPo9WyLQnaNdDWF3ydt/D9e146Ffoh+o8fnXr0A= +github.com/kuzgoga/fogg v0.1.0/go.mod h1:x0cKa6kIaweLKtzMWXw0xZZnl2PrLDpMmi+yL3xEIEg= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= diff --git a/main.go b/main.go index 5cb794a..4070110 100644 --- a/main.go +++ b/main.go @@ -4,21 +4,20 @@ import ( "log" "nto_cli/cmd" "nto_cli/generation" + "nto_cli/model" "nto_cli/utils" - "os" ) func main() { log.SetFlags(0) - structNames, path := cmd.SelectionInput() + modelsPath := utils.GetModelsPath() + models, err := model.ParseModelsPackage(modelsPath) + if err != nil { + log.Fatalf("Failed to parse models: %s", err) + } + selectedModels := cmd.SelectionInput(models) - for _, structName := range structNames { - file, err := os.Open(path) - if err != nil { - log.Fatalf("Failed to open file: %s", err) - } - structFields := utils.GetStructFields(file, structName) - _ = file.Close() - generation.Generate(structName, structFields) + for _, m := range *selectedModels { + generation.Generate(&m) } } diff --git a/entities/gen_field.go b/model/gen_field.go similarity index 54% rename from entities/gen_field.go rename to model/gen_field.go index 8fee312..9f3dc4a 100644 --- a/entities/gen_field.go +++ b/model/gen_field.go @@ -1,4 +1,4 @@ -package entities +package model import ( "bytes" @@ -7,31 +7,29 @@ import ( ) const fieldTemplate = `{ -{{ range .Metadata }}{{ if eq .Name "hidden" }} hidden: true, -{{ else if eq .Name "label" }} russian: "{{ index .Values 0 }}", -{{ else if eq .Name "readonly" }} readonly: true, -{{ end }}{{ end }}{{ if .IsArray }} many: true, +{{ if .Metadata.Hidden }} hidden: true, +{{ end }}{{ if .Metadata.Label }} russian: "{{ .Metadata.Label }}", +{{ end }}{{ if .Metadata.Readonly }} readonly: true, +{{ end }}{{ if .IsArray }} many: true, {{ end }}{{ .GeneratedType }} }` type FieldTemplateContext struct { - Metadata []Metadata + Metadata FieldMetadata IsArray bool GeneratedType string } -func (f *Field) Generate() string { +func (field *Field) GenerateFieldCode() string { tmpl, err := template.New("field").Parse(fieldTemplate) if err != nil { panic(fmt.Sprintf("Error parsing field template: %v", err)) } - isArray := len(f.Type) >= 2 && f.Type[0:2] == "[]" - data := FieldTemplateContext{ - Metadata: f.Metadata, - IsArray: isArray, - GeneratedType: f.GenerateType(), + Metadata: field.Metadata, + IsArray: field.Metadata.IsSlice, + GeneratedType: field.GenerateType(), } var result bytes.Buffer diff --git a/entities/gen_field_type.go b/model/gen_field_type.go similarity index 74% rename from entities/gen_field_type.go rename to model/gen_field_type.go index 1a997ef..6755a7a 100644 --- a/entities/gen_field_type.go +++ b/model/gen_field_type.go @@ -1,16 +1,14 @@ -package entities +package model import ( "bytes" "fmt" - "slices" + "go/ast" "strings" "text/template" ) var PrimitiveTypes = map[string]string{ - "date": "date", - "number": "number", "string": "string", "boolean": "boolean", "bool": "boolean", @@ -43,7 +41,7 @@ type TypeTemplateContext struct { Field string } -func (f *Field) GenerateType() string { +func (field *Field) GenerateType() string { keys := make([]string, 0, len(PrimitiveTypes)) for k := range PrimitiveTypes { keys = append(keys, k) @@ -51,19 +49,13 @@ func (f *Field) GenerateType() string { var data TypeTemplateContext - if slices.Contains(keys, strings.ToLower(f.Type)) { + if field.Metadata.IsPrimitiveType { data.IsPrimitive = true - data.PrimitiveType = PrimitiveTypes[strings.ToLower(f.Type)] + typeName := field.Type.(*ast.Ident).Name + data.PrimitiveType = PrimitiveTypes[typeName] } else { data.IsPrimitive = false - field := "[]" - for _, meta := range f.Metadata { - if meta.Name == "field" && len(meta.Values) > 0 { - field = "['" + strings.Join(meta.Values, "', '") + "']" - break - } - } - data.Field = field + data.Field = "['" + strings.Join(field.Metadata.RelatedFields, "', '") + "']" } tmpl, err := template.New("type").Parse(typeTemplate) diff --git a/model/get_not_implemented_models.go b/model/get_not_implemented_models.go new file mode 100644 index 0000000..ea34037 --- /dev/null +++ b/model/get_not_implemented_models.go @@ -0,0 +1,13 @@ +package model + +import "nto_cli/utils" + +func GetNotImplementedModels(models []Model) []Model { + var unimplementedModels []Model + for _, m := range models { + if !utils.IsEntityImplemented(m.Name) { + unimplementedModels = append(unimplementedModels, m) + } + } + return unimplementedModels +} diff --git a/model/model.go b/model/model.go new file mode 100644 index 0000000..1e3f7fb --- /dev/null +++ b/model/model.go @@ -0,0 +1,27 @@ +package model + +import "go/ast" + +type Model struct { + Name string + Fields []Field +} + +type Field struct { + Name string + Type ast.Expr + Tag string + Metadata FieldMetadata +} + +type FieldMetadata struct { + Hidden bool + Readonly bool + Label string + IsSlice bool + IsPrimitiveType bool + IsRelatedModel bool + RelatedModel string + Datatype string + RelatedFields []string +} diff --git a/model/parse_models.go b/model/parse_models.go new file mode 100644 index 0000000..21401d4 --- /dev/null +++ b/model/parse_models.go @@ -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 + } + } + } +} diff --git a/utils/contains_many.go b/utils/contains_many.go deleted file mode 100644 index a049820..0000000 --- a/utils/contains_many.go +++ /dev/null @@ -1,15 +0,0 @@ -package utils - -import "strings" - -func ContainsMany(str string, substrings ...string) bool { - var matches int - for _, substr := range substrings { - if strings.Contains(str, substr) { - matches++ - } else { - return false - } - } - return matches == len(substrings) -} diff --git a/utils/get_models_path.go b/utils/get_models_path.go new file mode 100644 index 0000000..fe5e9d7 --- /dev/null +++ b/utils/get_models_path.go @@ -0,0 +1,14 @@ +package utils + +import ( + "log" + "os" +) + +func GetModelsPath() string { + if len(os.Args) == 1 { + log.Fatalf("Please provide path to models.go") + } + + return os.Args[1] +} diff --git a/utils/get_not_implemented_structs.go b/utils/get_not_implemented_structs.go deleted file mode 100644 index b91b91f..0000000 --- a/utils/get_not_implemented_structs.go +++ /dev/null @@ -1,11 +0,0 @@ -package utils - -func GetNotImplementedStructs(modelsFilePath string) []string { - var models []string - for _, model := range GetStructsList(modelsFilePath) { - if !IsEntityImplemented(model) { - models = append(models, model) - } - } - return models -} diff --git a/utils/get_struct_fields.go b/utils/get_struct_fields.go deleted file mode 100644 index f5979d4..0000000 --- a/utils/get_struct_fields.go +++ /dev/null @@ -1,40 +0,0 @@ -package utils - -import ( - "bufio" - "log" - "nto_cli/entities" - "os" - "strings" -) - -func GetStructFields(file *os.File, structName string) []entities.Field { - bracketsCount := 1 - var structFound bool - var structFields []entities.Field - - scanner := bufio.NewScanner(file) - for i := 1; scanner.Scan() && bracketsCount > 0; i++ { - line := scanner.Text() - if ContainsMany(line, structName, "type", "struct") { - structFound = true - } - if structFound { - bracketsCount += strings.Count(line, "{") - bracketsCount -= strings.Count(line, "}") - line = strings.TrimSpace(line) - newField, err := SplitStructField(line) - if err != nil { - return structFields - } - if newField != nil { - structFields = append(structFields, *newField) - } - } - } - - if err := scanner.Err(); err != nil { - log.Fatalf("Failed to read file with scanner: %s", err) - } - return structFields -} diff --git a/utils/get_structs_list.go b/utils/get_structs_list.go deleted file mode 100644 index 8d4eb48..0000000 --- a/utils/get_structs_list.go +++ /dev/null @@ -1,36 +0,0 @@ -package utils - -import ( - "bufio" - "log" - "os" - "strings" -) - -func GetStructsList(modelsFilePath string) []string { - file, err := os.Open(modelsFilePath) - defer file.Close() - if err != nil { - log.Fatalf("Failed to open a file: %s", err) - } - - var structNames []string - s := bufio.NewScanner(file) - for s.Scan() { - line := s.Text() - if strings.Contains(line, "type ") && strings.Contains(line, " struct") { - start := strings.Index(line, "type ") + 5 - end := strings.Index(line, " struct") - name := strings.TrimSpace(line[start:end]) - if name != "" { - structNames = append(structNames, name) - } - } - } - - if s.Err() != nil { - log.Fatalf("Unexpected scanner error: %s", err) - } - - return structNames -} diff --git a/utils/is_primitive_type.go b/utils/is_primitive_type.go new file mode 100644 index 0000000..bbc29d8 --- /dev/null +++ b/utils/is_primitive_type.go @@ -0,0 +1,63 @@ +package utils + +import "go/ast" + +var primitives = map[string]bool{ + "bool": true, + "string": true, + "int": true, + "int8": true, + "int16": true, + "int32": true, + "int64": true, + "uint": true, + "uint8": true, + "uint16": true, + "uint32": true, + "uint64": true, + "uintptr": true, + "byte": true, + "rune": true, + "float32": true, + "float64": true, + "complex64": true, + "complex128": true, +} + +func IsPrimitiveType(expr ast.Expr) bool { + if ident, ok := expr.(*ast.Ident); ok { + return primitives[ident.Name] + } + + if _, ok := expr.(*ast.ArrayType); ok { + // Arrays and slices are not primitives + return false + } + + if _, ok := expr.(*ast.MapType); ok { + // Maps are not primitives + return false + } + + if _, ok := expr.(*ast.ChanType); ok { + // Channels are not primitives + return false + } + + if _, ok := expr.(*ast.StructType); ok { + // Structs are not primitives + return false + } + + if _, ok := expr.(*ast.StarExpr); ok { + // Handle pointers + return IsPrimitiveType(expr.(*ast.StarExpr).X) + } + + if _, ok := expr.(*ast.SelectorExpr); ok { + // Handle selector expressions (like pkg.Type) + return false + } + + return false +} diff --git a/utils/is_slice.go b/utils/is_slice.go new file mode 100644 index 0000000..c742b1d --- /dev/null +++ b/utils/is_slice.go @@ -0,0 +1,11 @@ +package utils + +import "go/ast" + +func IsSlice(expr ast.Expr) bool { + arrayType, ok := expr.(*ast.ArrayType) + if !ok { + return false + } + return arrayType.Len == nil +} diff --git a/utils/resolve_base_type.go b/utils/resolve_base_type.go new file mode 100644 index 0000000..cae9005 --- /dev/null +++ b/utils/resolve_base_type.go @@ -0,0 +1,21 @@ +package utils + +import ( + "go/ast" +) + +func ResolveBaseType(expr ast.Expr) *string { + switch e := expr.(type) { + case *ast.Ident: + return &e.Name + case *ast.StarExpr: + return ResolveBaseType(e.X) + case *ast.ArrayType: + return ResolveBaseType(e.Elt) + case *ast.SelectorExpr: + return ResolveBaseType(e.X) + case *ast.ParenExpr: + return ResolveBaseType(e.X) + } + return nil +} diff --git a/utils/split_by_single_space.go b/utils/split_by_single_space.go deleted file mode 100644 index 6ae883a..0000000 --- a/utils/split_by_single_space.go +++ /dev/null @@ -1,14 +0,0 @@ -package utils - -import "strings" - -func SplitBySingleSpace(input string) []string { - parts := strings.Split(strings.TrimSpace(input), " ") - var result []string - for _, p := range parts { - if p != "" { - result = append(result, p) - } - } - return result -} diff --git a/utils/split_struct_field.go b/utils/split_struct_field.go deleted file mode 100644 index 358128f..0000000 --- a/utils/split_struct_field.go +++ /dev/null @@ -1,59 +0,0 @@ -package utils - -import ( - "errors" - "log" - "nto_cli/entities" - "strings" - - "github.com/fatih/structtag" -) - -func SplitStructField(field string) (*entities.Field, error) { - if strings.Contains(field, "type") { - return nil, nil - } - if len(strings.TrimSpace(field)) < 2 { - return nil, errors.New("unexpected end of struct field") - } - startBacktick := strings.Index(field, "`") - endBacktick := -1 - - var metadata []entities.Metadata - - if startBacktick > -1 { - endBacktick = strings.Index(field[startBacktick+1:], "`") - if endBacktick > -1 { - endBacktick += startBacktick + 1 - structTag := field[startBacktick+1 : endBacktick] - tags, err := structtag.Parse(structTag) - if err != nil { - log.Fatalf("failed to parse struct tag: %s", err) - } - - if uiTag, e := tags.Get("ui"); e == nil { - uiTags := append([]string{uiTag.Name}, uiTag.Options...) - for _, t := range uiTags { - analyzed := entities.NewMetadata(t) - if analyzed != nil { - metadata = append(metadata, *analyzed) - } - } - } - } - } else { - startBacktick = len(field) - } - - field = strings.TrimSpace(field[:startBacktick]) - - data := SplitBySingleSpace(field) - - name := data[0] - dataType := data[1] - return &entities.Field{ - Metadata: metadata, - Type: dataType, - Name: name, - }, nil -}