-
Notifications
You must be signed in to change notification settings - Fork 3
/
extractor.go
135 lines (108 loc) · 3.02 KB
/
extractor.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package core
import (
"context"
"fmt"
"os"
"path/filepath"
cueast "cuelang.org/go/cue/ast"
"cuelang.org/go/cue/format"
"github.com/pkg/errors"
"golang.org/x/mod/sumdb/dirhash"
"github.com/octohelm/cuemod/internal/version"
)
type Extractor interface {
// Name
// Extractor name
// used in mod.cue @import("")
Name() string
// Detect check dir should use extractor.
// if matched, return deps <repo>:<version> too.
Detect(ctx context.Context, src string) (bool, map[string]string)
// Extract convert dir to cue codes
Extract(ctx context.Context, src string) ([]*cueast.File, error)
}
var SharedExtractors = Extractors{}
func Register(extractor Extractor) {
SharedExtractors.Register(extractor)
}
type Extractors map[string]Extractor
func (extractors Extractors) Register(extractor Extractor) {
extractors[extractor.Name()] = extractor
}
func (extractors Extractors) Detect(ctx context.Context, src string) (string, map[string]string) {
for _, e := range extractors {
if ok, deps := e.Detect(ctx, src); ok {
return e.Name(), deps
}
}
return "", nil
}
func (extractors Extractors) ExtractToDir(ctx context.Context, name string, src string, gen string) error {
if extractor, ok := extractors[name]; ok {
return extractors.do(ctx, extractor, src, gen)
}
return errors.Errorf("unsupport extractor `%s`", name)
}
func (Extractors) do(ctx context.Context, extractor Extractor, src string, gen string) error {
sumFile := filepath.Join(gen, ".sum")
sum, _ := os.ReadFile(sumFile)
origin, err := os.Readlink(src)
if err == nil {
src = origin
}
dirSum, err := dirhash.HashDir(src, "cuem-"+version.Version(), dirhash.DefaultHash)
if err != nil {
return err
}
if string(sum) == dirSum {
// skip when dirSum same
return nil
}
currentFiles, err := filepath.Glob(filepath.Join(gen, "*_gen.cue"))
if err != nil {
return err
}
shouldDelete := map[string]bool{}
for _, f := range currentFiles {
shouldDelete[filepath.Base(f)] = true
}
files, err := extractor.Extract(ctx, src)
if err != nil {
return err
}
for i := range files {
shouldDelete[files[i].Filename] = false
if err := writeCueFile(ctx, extractor.Name(), gen, files[i]); err != nil {
return err
}
}
for filename, ok := range shouldDelete {
if ok {
if err := os.RemoveAll(filepath.Join(gen, filename)); err != nil {
return err
}
}
}
return writeFile(sumFile, []byte(dirSum))
}
func writeCueFile(ctx context.Context, name string, dir string, f *cueast.File) error {
filename := filepath.Join(dir, f.Filename)
cueast.AddComment(f.Decls[0], &cueast.CommentGroup{
Doc: true,
List: []*cueast.Comment{
{Text: "// DO NOT EDIT THIS FILE DIRECTLY."},
{Text: fmt.Sprintf("// generated by %s extractor.", name)},
},
})
data, err := format.Node(f, format.Simplify())
if err != nil {
return err
}
return writeFile(filename, data)
}
func writeFile(filename string, data []byte) error {
if err := os.MkdirAll(filepath.Dir(filename), os.ModePerm); err != nil {
return err
}
return os.WriteFile(filename, data, os.ModePerm)
}