Skip to content

Commit e2a90b3

Browse files
committed
feat(adptool): add support for processing directories and multiple files
- Implement functionality to handle input directories and process all Go files within - Add ability to process multiple files specified as command-line arguments - Introduce new flag `-c` to specify configuration file - Remove unused `engine` package - Add new packages: `compiler`, `generator`, and `parser` - Implement new functions for file handling and adapter generation
1 parent 687b23e commit e2a90b3

File tree

2 files changed

+302
-13
lines changed

2 files changed

+302
-13
lines changed

cmd/adptool/main.go

Lines changed: 177 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,157 @@ package main
22

33
import (
44
"flag"
5+
"fmt"
6+
"io/fs"
57
"log/slog"
68
"os"
79
"path/filepath"
10+
"strings"
811

12+
"github.com/origadmin/adptool/internal/compiler"
913
"github.com/origadmin/adptool/internal/config"
10-
"github.com/origadmin/adptool/internal/engine"
14+
"github.com/origadmin/adptool/internal/generator"
1115
"github.com/origadmin/adptool/internal/loader"
16+
"github.com/origadmin/adptool/internal/parser"
1217
)
1318

19+
// processFile processes a single Go file and generates its adapter
20+
func processFile(filePath string, cfg *config.Config) error {
21+
// First check if the file has the adapter directive
22+
hasAdapter, err := hasAdapterDirective(filePath)
23+
if err != nil {
24+
return fmt.Errorf("failed to check adapter directive in %s: %w", filePath, err)
25+
}
26+
27+
if !hasAdapter {
28+
slog.Debug("Skipping file without //go:adapter directive", "file", filePath)
29+
return nil
30+
}
31+
32+
// Parse the Go file to get the AST
33+
file, fset, err := loader.LoadGoFile(filePath)
34+
if err != nil {
35+
return fmt.Errorf("failed to load Go file %s: %w", filePath, err)
36+
}
37+
38+
// Parse file directives using the loaded config
39+
pkgConfig, err := parser.ParseFileDirectives(cfg, file, fset)
40+
if err != nil {
41+
return fmt.Errorf("failed to parse file directives in %s: %w", filePath, err)
42+
}
43+
44+
// Compile the configuration
45+
compiledCfg, err := compiler.Compile(pkgConfig)
46+
if err != nil {
47+
return fmt.Errorf("error compiling config for %s: %w", filePath, err)
48+
}
49+
50+
replacer := compiler.NewReplacer(compiledCfg)
51+
52+
// Set output file path (same directory as input file with .adapter.go suffix)
53+
dir := filepath.Dir(filePath)
54+
baseName := filepath.Base(filePath)
55+
ext := filepath.Ext(baseName)
56+
outputBase := baseName[:len(baseName)-len(ext)] + ".adapter.go"
57+
outputFile := filepath.Join(dir, outputBase)
58+
59+
// Convert PackageConfig to PackageInfo
60+
var packageInfos []*generator.PackageInfo
61+
for _, pkg := range pkgConfig.Packages {
62+
packageInfos = append(packageInfos, &generator.PackageInfo{
63+
ImportPath: pkg.Import,
64+
ImportAlias: pkg.Alias,
65+
})
66+
}
67+
68+
// Determine the package name
69+
packageName := pkgConfig.PackageName
70+
if packageName == "" {
71+
packageName = filepath.Base(dir)
72+
}
73+
74+
// Generate the adapter file
75+
gen := generator.NewGenerator(packageName, outputFile, replacer).
76+
WithNoEditHeader(true)
77+
78+
if err := gen.Generate(packageInfos); err != nil {
79+
return fmt.Errorf("error generating adapter file %s: %w", outputFile, err)
80+
}
81+
82+
slog.Info("Generated adapter file", "path", outputFile)
83+
return nil
84+
}
85+
86+
// hasAdapterDirective checks if the file contains //go:adapter directive
87+
func hasAdapterDirective(filePath string) (bool, error) {
88+
content, err := os.ReadFile(filePath)
89+
if err != nil {
90+
return false, fmt.Errorf("failed to read file %s: %w", filePath, err)
91+
}
92+
return strings.Contains(string(content), parser.DirectivePrefix), nil
93+
}
94+
95+
// findGoFiles finds all .go files in the given directory that contain //go:adapter directive
96+
func findGoFiles(dir string) ([]string, error) {
97+
// Handle current directory (.) case
98+
if dir == "." {
99+
var err error
100+
dir, err = os.Getwd()
101+
if err != nil {
102+
return nil, fmt.Errorf("failed to get current directory: %w", err)
103+
}
104+
}
105+
106+
var files []string
107+
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
108+
if err != nil {
109+
return err
110+
}
111+
112+
// Skip directories, test files, and non-Go files
113+
if d.IsDir() ||
114+
strings.HasSuffix(d.Name(), "_test.go") ||
115+
!strings.HasSuffix(d.Name(), ".go") ||
116+
strings.HasPrefix(d.Name(), ".") {
117+
return nil
118+
}
119+
120+
// Check if file contains //go:adapter directive
121+
hasAdapter, err := hasAdapterDirective(path)
122+
if err != nil {
123+
slog.Warn("Failed to check adapter directive", "file", path, "error", err)
124+
return nil
125+
}
126+
127+
if hasAdapter {
128+
files = append(files, path)
129+
}
130+
131+
return nil
132+
})
133+
134+
if err != nil {
135+
return nil, fmt.Errorf("error walking directory %s: %w", dir, err)
136+
}
137+
138+
return files, nil
139+
}
140+
14141
func main() {
15-
configFile := flag.String("f", "", "Configuration file (YAML/JSON). If specified, it completely replaces adptool.yaml.")
142+
configFile := flag.String("c", "", "Configuration file (YAML/JSON). If specified, it completely replaces adptool.yaml.")
16143
flag.Parse()
17144

18-
// Get the input Go file from command line arguments
145+
// Get the input path from command line arguments
19146
args := flag.Args()
20147
if len(args) == 0 {
21-
slog.Error("No input Go file specified")
148+
slog.Error("No input path specified")
22149
os.Exit(1)
23150
}
24151

25-
inputFile := args[0]
152+
inputPath := args[0]
26153

27-
// Get absolute path to the input file
28-
abspath, err := filepath.Abs(inputFile)
154+
// Get absolute path to the input path
155+
abspath, err := filepath.Abs(inputPath)
29156
if err != nil {
30157
slog.Error("Failed to get absolute path", "error", err)
31158
os.Exit(1)
@@ -41,16 +168,53 @@ func main() {
41168
slog.Error("Failed to load config file", "file", *configFile, "error", err)
42169
os.Exit(1)
43170
}
44-
// Merge the loaded config with defaults
171+
// Use the loaded config
45172
cfg = fileCfg
46173
}
47174

48-
// Create and use the new engine
49-
e := engine.New()
50-
if err := e.ExecuteFile(abspath, cfg); err != nil {
51-
slog.Error("Failed to execute engine", "error", err)
175+
// Check if the input is a directory or a file
176+
fileInfo, err := os.Stat(abspath)
177+
if err != nil {
178+
slog.Error("Failed to get file info", "path", abspath, "error", err)
52179
os.Exit(1)
53180
}
54181

55-
slog.Info("Successfully generated adapter file", "path", abspath)
182+
var filesToProcess []string
183+
184+
if fileInfo.IsDir() {
185+
// If it's a directory, find all .go files
186+
files, err := findGoFiles(abspath)
187+
if err != nil {
188+
slog.Error("Failed to find Go files in directory", "directory", abspath, "error", err)
189+
os.Exit(1)
190+
}
191+
192+
if len(files) == 0 {
193+
slog.Info("No Go files found in directory", "directory", abspath)
194+
return
195+
}
196+
197+
filesToProcess = files
198+
} else {
199+
// If it's a single file, just process that file
200+
if !strings.HasSuffix(abspath, ".go") {
201+
slog.Error("Input file is not a Go file", "file", abspath)
202+
os.Exit(1)
203+
}
204+
filesToProcess = []string{abspath}
205+
}
206+
207+
// Process each file
208+
var hasErrors bool
209+
for _, file := range filesToProcess {
210+
if err := processFile(file, cfg); err != nil {
211+
slog.Error("Error processing file", "file", file, "error", err)
212+
hasErrors = true
213+
}
214+
}
215+
216+
if hasErrors {
217+
slog.Error("Failed to process some files")
218+
os.Exit(1)
219+
}
56220
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package aliaspkg
2+
3+
import (
4+
pkg1 "github.com/origadmin/adptool/testdata/duplicate/pkg1"
5+
pkg2 "github.com/origadmin/adptool/testdata/duplicate/pkg2"
6+
pkg3 "github.com/origadmin/adptool/testdata/duplicate/pkg3"
7+
sourcepkg1 "github.com/origadmin/adptool/testdata/duplicate/sourcepkg"
8+
sourcepkg21 "github.com/origadmin/adptool/testdata/duplicate/sourcepkg2"
9+
sourcepkg3 "github.com/origadmin/adptool/testdata/duplicate/sourcepkg3"
10+
sourcePkg4 "github.com/origadmin/adptool/testdata/source-pkg4"
11+
sourcepkg "github.com/origadmin/adptool/testdata/sourcepkg"
12+
sourcepkg2 "github.com/origadmin/adptool/testdata/sourcepkg2"
13+
custompkg "github.com/origadmin/adptool/testdata/sourcepkg3"
14+
)
15+
16+
const (
17+
MaxRetries = custompkg.MaxRetries
18+
StatusUnknown = custompkg.StatusUnknown
19+
StatusPending = custompkg.StatusPending
20+
StatusRunning = custompkg.StatusRunning
21+
StatusSuccess = custompkg.StatusSuccess
22+
StatusFailed = custompkg.StatusFailed
23+
PriorityLow = custompkg.PriorityLow
24+
PriorityMedium = custompkg.PriorityMedium
25+
PriorityHigh = custompkg.PriorityHigh
26+
DefaultTimeout = custompkg.DefaultTimeout
27+
Version = custompkg.Version
28+
MaxRetries1 = sourcepkg.MaxRetries
29+
ExportedConstant = sourcepkg.ExportedConstant
30+
MaxRetries2 = sourcepkg2.MaxRetries
31+
DefaultTimeout1 = sourcepkg2.DefaultTimeout
32+
Version1 = sourcepkg2.Version
33+
)
34+
35+
var (
36+
ConfigValue = custompkg.ConfigValue
37+
DefaultWorker = custompkg.DefaultWorker
38+
StatsCounter = custompkg.StatsCounter
39+
Processors = custompkg.Processors
40+
ConfigValue1 = sourcepkg.ConfigValue
41+
ExportedVariable = sourcepkg.ExportedVariable
42+
ConfigValue2 = sourcepkg2.ConfigValue
43+
DefaultWorker1 = sourcepkg2.DefaultWorker
44+
StatsCounter1 = sourcepkg2.StatsCounter
45+
)
46+
47+
type (
48+
CommonStruct = custompkg.CommonStruct
49+
ComplexGenericInterface[T any, K comparable] = custompkg.ComplexGenericInterface[T, K]
50+
EmbeddedInterface = custompkg.EmbeddedInterface
51+
InputData[T any] = custompkg.InputData[T]
52+
OutputData = custompkg.OutputData
53+
Worker = custompkg.Worker
54+
WorkerConfig = custompkg.WorkerConfig
55+
GenericWorker[T any] = custompkg.GenericWorker[T]
56+
ProcessFunc = custompkg.ProcessFunc
57+
HandlerFunc[T any] = custompkg.HandlerFunc[T]
58+
ProcessOption = custompkg.ProcessOption
59+
ProcessConfig = custompkg.ProcessConfig
60+
WorkerOption = custompkg.WorkerOption
61+
Status = custompkg.Status
62+
Priority = custompkg.Priority
63+
TimeAlias = custompkg.TimeAlias
64+
StatusAlias = custompkg.StatusAlias
65+
IntAlias = custompkg.IntAlias
66+
User1 = pkg1.User
67+
UserService = pkg1.UserService
68+
Product1 = pkg1.Product
69+
ProductService = pkg1.ProductService
70+
User2 = pkg2.User
71+
UserService1 = pkg2.UserService
72+
Product2 = pkg3.Product
73+
ProductService1 = pkg3.ProductService
74+
Handler = sourcePkg4.Handler
75+
GenericHandler[T any] = sourcePkg4.GenericHandler[T]
76+
Model = sourcePkg4.Model
77+
Data = sourcePkg4.Data
78+
Service = sourcePkg4.Service
79+
CommonStruct1 = sourcepkg.CommonStruct
80+
MyStruct = sourcepkg.MyStruct
81+
ExportedType = sourcepkg.ExportedType
82+
ExportedInterface = sourcepkg.ExportedInterface
83+
Product3 = sourcepkg1.Product
84+
ProductService2 = sourcepkg1.ProductService
85+
User3 = sourcepkg1.User
86+
UserService2 = sourcepkg1.UserService
87+
CommonStruct2 = sourcepkg2.CommonStruct
88+
ComplexInterface = sourcepkg2.ComplexInterface
89+
InputData1 = sourcepkg2.InputData
90+
OutputData1 = sourcepkg2.OutputData
91+
Worker1 = sourcepkg2.Worker
92+
User4 = sourcepkg21.User
93+
UserService3 = sourcepkg21.UserService
94+
User = sourcepkg3.User
95+
Product = sourcepkg3.Product
96+
CommonService = sourcepkg3.CommonService
97+
)
98+
99+
func CommonFunction() string {
100+
return custompkg.CommonFunction()
101+
}
102+
func NewWorker(name string, options ...custompkg.WorkerOption) *custompkg.Worker {
103+
return custompkg.NewWorker(name, options...)
104+
}
105+
func NewGenericWorker[T any](name string, data T, processor func(T) error) *custompkg.GenericWorker[T] {
106+
return custompkg.NewGenericWorker[T](name, data, processor)
107+
}
108+
func Map[T, U any](ts []T, fn func(T) U) []U {
109+
return custompkg.Map[T, U](ts, fn)
110+
}
111+
func Filter[T any](ts []T, fn func(T) bool) []T {
112+
return custompkg.Filter[T](ts, fn)
113+
}
114+
func CommonFunction1() string {
115+
return sourcepkg.CommonFunction()
116+
}
117+
func ExportedFunction() {
118+
sourcepkg.ExportedFunction()
119+
}
120+
func CommonFunction2() string {
121+
return sourcepkg2.CommonFunction()
122+
}
123+
func NewWorker1(name string) *sourcepkg2.Worker {
124+
return sourcepkg2.NewWorker(name)
125+
}

0 commit comments

Comments
 (0)