/
scan.go
105 lines (89 loc) · 2.66 KB
/
scan.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package analyze
import (
"fmt"
"go/ast"
"go/build"
"path/filepath"
"strings"
"github.com/xaionaro-go/gosrc"
)
// Scan holds information about a scan process used to scan a path.
func Scan(
path string,
goPaths []string,
) (*Package, error) {
// Scan the path "path".
pkgRaw, err := getRawPkg(path, goPaths)
if err != nil {
return nil, fmt.Errorf("unable to get the package from directory '%s': %w", path, err)
}
// Basically just converting received structures to our-own types,
// which we will used then in cmd/manifestcodegen/template_methods.go.
pkg, err := convertRawPkg(pkgRaw)
if err != nil {
return nil, fmt.Errorf("unable to get convert the package: %w", err)
}
return pkg, nil
}
func getRawPkg(
path string,
goPaths []string,
) (*gosrc.Package, error) {
buildCtx := build.Default
buildCtx.GOPATH = strings.Join(goPaths, string(filepath.ListSeparator))
buildCtx.BuildTags = append(buildCtx.BuildTags, `manifestcodegen`)
dirRaw, err := gosrc.OpenDirectoryByPkgPath(&buildCtx, path, false, false, false, nil)
if err != nil {
return nil, fmt.Errorf("unable to open go directory: %w", err)
}
if len(dirRaw.Packages) != 1 {
return nil, fmt.Errorf("expected one package in the directory, but received %d", len(dirRaw.Packages))
}
return dirRaw.Packages[0], nil
}
func convertRawPkg(
pkgRaw *gosrc.Package,
) (*Package, error) {
pkg := &Package{
Package: *pkgRaw,
}
for _, fileRaw := range pkgRaw.Files.FilterByGoGenerateTag("manifestcodegen") {
file := &File{
File: *fileRaw,
Parent: pkg,
Structs: map[string]*Struct{},
BasicNamedTypes: map[string]*BasicNamedType{},
}
for _, astTypeSpec := range fileRaw.AstTypeSpecs() {
switch astTypeSpec.TypeSpec.Type.(type) {
case *ast.StructType:
structRaw := &gosrc.Struct{AstTypeSpec: *astTypeSpec}
fieldsRaw, err := structRaw.Fields()
if err != nil {
return nil, fmt.Errorf("unable to get fields of struct '%s': %w", structRaw, err)
}
_struct := &Struct{
Struct: *structRaw,
Parent: file,
}
for _, fieldRaw := range fieldsRaw {
_struct.Fields = append(_struct.Fields, &Field{
Field: *fieldRaw,
Parent: _struct,
})
}
if _struct := pkg.StructByName(_struct.Name()); _struct != nil {
return nil, fmt.Errorf("structure %s is defined twice (files: '%s' and '%s')", _struct.Name(), _struct.Parent.File, file.Path)
}
file.Structs[_struct.Name()] = _struct
case *ast.Ident:
file.BasicNamedTypes[astTypeSpec.TypeSpec.Name.Name] = &BasicNamedType{
Parent: file,
AstTypeSpec: *astTypeSpec,
}
}
}
pkg.Files = append(pkg.Files, file)
}
return pkg, nil
}