package main import ( "bufio" "flag" "fmt" "os" "os/exec" "path" "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 UseExt bool } func parseDefinitions(defFileName, outFileName, pkgName string) (err try.Err) { defer try.Annotate(&err, "error parsing definitions") defFile := try.File(os.Open(defFileName)) useExt := pkgName != "try" 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 } lineData.UseExt = useExt 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 PkgName string UseExt bool }{ Definitions: lines, Imports: imports, PkgName: pkgName, UseExt: useExt, })) try.Check(defFile.Close()) try.Check(outFile.Close()) try.Check(goFmt(outFileName)) return } func parseLine(lineStr string, imports map[string]bool) (*line, try.Err) { lineParts := strings.Split(lineStr, ";") lineData := line{} if len(lineParts) < 2 { return nil, try.NewErr("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, try.NewErr("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, try.NewErr("invalid type name") } // Parse imports for i := 2; i < len(lineParts); i++ { pkgName := strings.TrimSpace(lineParts[i]) imports[pkgName] = true } return &lineData, nil } func goFmt(goFile string) error { return exec.Command("gofmt", "-s", "-w", goFile).Run() } func main() { defer try.CatchTrace(1) // 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") flag.Parse() try.Check(os.MkdirAll(targetDir, 0o777)) // Generate types if _, e := os.Stat(definitionFile); os.IsNotExist(e) { try.Check(try.NewErr("Definition file does not exist")) } else { try.Check( parseDefinitions(definitionFile, path.Join(targetDir, typeFile+".go"), path.Base(targetDir))) } }