gotry/gotry_generate/main.go
Theta-Dev 30f9e9661d
All checks were successful
continuous-integration/drone/push Build is passing
Fix gofmt issue
2021-10-14 10:27:49 +02:00

204 lines
4.7 KiB
Go

package main
import (
"bufio"
"errors"
"flag"
"fmt"
"io"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
"text/template"
"code.thetadev.de/ThetaDev/gotry"
"code.thetadev.de/ThetaDev/gotry/try"
)
var variablePattern = regexp.MustCompile(`^(\w[\w\d]*)(\+s)?$`)
type line struct {
Name string
WithSlice bool
Types []string
}
func parseDefinitions(defFileName string, outFileName string) (err error) {
defer try.Annotate("error parsing definitions", &err)
defFile := try.File(os.Open(defFileName))
fileScanner := bufio.NewScanner(defFile)
fileScanner.Split(bufio.ScanLines)
ln := 0
var lines []line
imports := map[string]bool{}
for fileScanner.Scan() {
lineStr := strings.TrimSpace(fileScanner.Text())
ln++
if lineStr == "" || strings.HasPrefix(lineStr, "#") ||
strings.HasPrefix(lineStr, "/") {
continue
}
lineData, err := parseLine(lineStr, imports)
if err != nil {
fmt.Printf("[Err] line %d, %s: \"%s\n", ln, err.Error(), lineStr)
continue
}
lines = append(lines, *lineData)
}
fmt.Printf("Parsed %d definitions\n", len(lines))
outFile := try.File(os.Create(outFileName))
tmpl := template.Must(template.New("templ").Parse(gotry.TypesTemplate))
try.Check(tmpl.Execute(outFile, struct {
Definitions []line
Imports map[string]bool
}{
Definitions: lines,
Imports: imports,
}))
try.Check(defFile.Close())
try.Check(outFile.Close())
try.Check(goFmt(outFileName))
return
}
func parseLine(lineStr string, imports map[string]bool) (*line, error) {
lineParts := strings.Split(lineStr, ";")
lineData := line{}
if len(lineParts) < 2 {
return nil, errors.New("missing type name")
}
// Parse variable name
variableRaw := strings.TrimSpace(lineParts[0])
if match := variablePattern.FindStringSubmatch(variableRaw); match != nil {
lineData.Name = match[1]
lineData.WithSlice = match[2] != ""
} else {
return nil, errors.New("invalid variable name")
}
// Parse type name(s)
for _, typeStr := range strings.Split(lineParts[1], ",") {
typeStr = strings.TrimSpace(typeStr)
if typeStr != "" {
lineData.Types = append(lineData.Types, typeStr)
}
}
if len(lineData.Types) == 0 {
return nil, errors.New("invalid type name")
}
// Parse imports
for i := 2; i < len(lineParts); i++ {
pkgName := strings.TrimSpace(lineParts[i])
imports[pkgName] = true
}
return &lineData, nil
}
func copyLibrary(targetDir string, noTests, noStds bool) (caught error) {
defer try.Return(&caught)
filesToCopy := []string{"try.go"}
if !noTests {
filesToCopy = append(filesToCopy, "try_test.go")
}
if !noStds {
filesToCopy = append(filesToCopy, "std_types.go")
if !noTests {
filesToCopy = append(filesToCopy, "std_types_test.go")
}
}
for _, fname := range filesToCopy {
f, e := gotry.TryFiles.Open("try/" + fname)
try.Check(e)
targetPath := filepath.Join(targetDir, fname)
dest := try.File(os.Create(targetPath))
try.Empty(io.Copy(dest, f))
fmt.Printf("Copied %s\n", fname)
try.Check(f.Close())
try.Check(dest.Close())
}
// Add go-generate command
gencmd := "\n//go:generate go run code.thetadev.de/ThetaDev/gotry/gotry_generate " +
"-def types.csv -o .\n"
f := try.File(
os.OpenFile(filepath.Join(targetDir, "try.go"), os.O_APPEND|os.O_WRONLY, 0o644))
try.Empty(f.WriteString(gencmd))
try.Check(f.Close())
// If not present, add definition file template
definitionFile := filepath.Join(targetDir, "types.csv")
if _, e := os.Stat(definitionFile); os.IsNotExist(e) {
f := try.File(os.Create(definitionFile))
try.Int(f.WriteString(
"# Add your type definitions here\n# Example:\n# DB;*gorm.DB;gorm.io/gorm\n"))
try.Check(f.Close())
}
return
}
func goFmt(goFile string) error {
return exec.Command("gofmt", "-s", "-w", goFile).Run()
}
func main() {
defer try.Catch(func(err error) {
fmt.Println("Error: " + err.Error())
})
// Read flags
var definitionFile string
var targetDir string
var typeFile string
flag.StringVar(&definitionFile, "def", path.Join("try", "types.csv"),
"Definition file")
flag.StringVar(&targetDir, "o", "try", "Target directory")
flag.StringVar(&typeFile, "of", "types", "Name of generated type definition file")
flagInit := flag.Bool("init", false, "Copy gotry package")
flagNoTests := flag.Bool("no-tests", false, "Dont copy tests")
flagNoStds := flag.Bool("no-std", false, "Dont copy std types")
flag.Parse()
try.Check(os.MkdirAll(targetDir, 0o777))
// Copy library
if *flagInit {
try.Check(copyLibrary(targetDir, *flagNoTests, *flagNoStds))
}
// Generate types
if _, e := os.Stat(definitionFile); os.IsNotExist(e) {
fmt.Println("Definition file does not exist")
} else {
try.Check(parseDefinitions(definitionFile, path.Join(targetDir, typeFile+".go")))
}
}