talos

Форк
0
173 строки · 4.3 Кб
1
// This Source Code Form is subject to the terms of the Mozilla Public
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4

5
// structprotogen is a tool to generate proto files from Go structs.
6
package main
7

8
//nolint:gci
9
import (
10
	"fmt"
11
	"os"
12
	"path"
13
	"path/filepath"
14

15
	"github.com/spf13/cobra"
16

17
	"github.com/siderolabs/structprotogen/ast"
18
	"github.com/siderolabs/structprotogen/consts"
19
	"github.com/siderolabs/structprotogen/loader"
20
	"github.com/siderolabs/structprotogen/proto"
21
	"github.com/siderolabs/structprotogen/types"
22
)
23

24
// rootCmd represents the base command when called without any subcommands.
25
var rootCmd = &cobra.Command{
26
	Use:     "structprotogen path dest",
27
	Short:   "This CLI is used to generate proto files from Go structs into one proto file",
28
	Example: "gotagsrewrite github.com/siderolabs/talos/pkg/machinery/resources/... ./api/resource/definitions",
29
	Args:    cobra.ExactArgs(2),
30
	Version: "v1.0.0",
31
	RunE: func(cmd *cobra.Command, args []string) error {
32
		return run(args[0], args[1])
33
	},
34
	SilenceUsage:      true,
35
	DisableAutoGenTag: true,
36
}
37

38
func main() {
39
	err := rootCmd.Execute()
40
	if err != nil {
41
		os.Exit(1)
42
	}
43
}
44

45
// TODO(DmitriyMV): get comments for fields
46

47
//nolint:gocyclo
48
func run(pkgPath, dst string) error {
49
	loadedPkgs, err := loader.LoadPackages(pkgPath)
50
	if err != nil {
51
		return err
52
	}
53

54
	constants, err := consts.FindIn(loadedPkgs)
55
	if err != nil {
56
		return err
57
	}
58

59
	taggedStructs := ast.FindAllTaggedStructs(loadedPkgs)
60
	printFoundStructs(taggedStructs)
61

62
	sortedPkgs, err := types.FindPkgDecls(taggedStructs, loadedPkgs)
63
	if err != nil {
64
		return fmt.Errorf("error finding path '%s' declarations: %w", pkgPath, err)
65
	}
66

67
	pkgsTypes, err := types.ParseDeclsData(sortedPkgs, taggedStructs)
68
	if err != nil {
69
		return fmt.Errorf("error parsing path '%s' declarations data: %w", pkgPath, err)
70
	}
71

72
	externalTypes := types.FindExternalTypes(pkgsTypes, taggedStructs)
73
	for i := 0; i < externalTypes.Len(); i++ {
74
		externalType := externalTypes.Get(i)
75

76
		if constants.HaveType(externalType.Pkg, externalType.Name) {
77
			continue
78
		}
79

80
		if !proto.IsSupportedExternalType(externalType) {
81
			return fmt.Errorf("external type '%s.%s' is not supported", externalType.Pkg, externalType.Name)
82
		}
83
	}
84

85
	data := proto.PrepareProtoData(pkgsTypes, constants)
86

87
	for i := 0; i < data.Len(); i++ {
88
		protoData := data.Get(i)
89

90
		fmt.Println("--------")
91
		protoData.WriteDebug(os.Stdout)
92
	}
93

94
	err = os.MkdirAll(dst, 0o755)
95
	if err != nil {
96
		return fmt.Errorf("failed to create directory for proto files: %w", err)
97
	}
98

99
	if len(constants) > 0 {
100
		err = withFile(filepath.Join(dst, "enums", "enums.proto"), func(f *os.File) error {
101
			return constants.FormatProtoFile(f)
102
		})
103
		if err != nil {
104
			return fmt.Errorf("failed to write enums proto file: %w", err)
105
		}
106
	}
107

108
	for i := 0; i < data.Len(); i++ {
109
		protoData := data.Get(i)
110

111
		dstDir, err := filepath.Abs(filepath.Join(dst, protoData.Name))
112
		if err != nil {
113
			return fmt.Errorf("failed to get absolute path for pkg '%s': %w", protoData.GoPkg, err)
114
		}
115

116
		err = os.MkdirAll(dstDir, 0o755)
117
		if err != nil {
118
			return fmt.Errorf("failed to create directory for pkg '%s' proto files: %w", protoData.GoPkg, err)
119
		}
120

121
		dstFile, err := filepath.Abs(filepath.Join(dstDir, path.Base(protoData.GoPkg)+".proto"))
122
		if err != nil {
123
			return fmt.Errorf("failed to get absolute path for destination file: %w", err)
124
		}
125

126
		fmt.Println("writing file", dstFile)
127

128
		err = withFile(dstFile, func(f *os.File) error {
129
			protoData.Format(f)
130

131
			return nil
132
		})
133
		if err != nil {
134
			return fmt.Errorf("failed to write file '%s': %w", dstFile, err)
135
		}
136
	}
137

138
	return nil
139
}
140

141
func printFoundStructs(structs ast.TaggedStructs) {
142
	for decl := range structs {
143
		fmt.Printf("found tagged struct '%s' in pkg '%s'\n", decl.Name, decl.Pkg)
144
	}
145
}
146

147
func withFile(filename string, fn func(f *os.File) error) error {
148
	dir := filepath.Dir(filename)
149

150
	_, err := os.Stat(dir)
151
	if os.IsNotExist(err) {
152
		err = os.MkdirAll(dir, 0o755)
153
		if err != nil {
154
			return err
155
		}
156
	} else if err != nil {
157
		return err
158
	}
159

160
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm)
161
	if err != nil {
162
		return err
163
	}
164

165
	defer func() {
166
		err := file.Close()
167
		if err != nil {
168
			fmt.Println("failed to close file:", err)
169
		}
170
	}()
171

172
	return fn(file)
173
}
174

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.