package main import "os" import "strings" import "path/filepath" import "git.tebibyte.media/sashakoshka/go-cli" import "git.tebibyte.media/sashakoshka/goparse" import "git.tebibyte.media/sashakoshka/hopp/generate" func main() { flagOutput := cli.NewInputFlag('o', "output", "The output file", "", cli.ValString) flagPackageName := cli.NewInputFlag('p', "package-name", "The package name of the file", "", cli.ValString) command := cli.New("Compile PDL files to program source code", flagOutput, flagPackageName) command.Syntax = "FILE [OPTIONS]..." command.ParseOrExit(os.Args) if len(command.Args) != 1 { command.Usage() os.Exit(2) } source := command.Args[0] destination := flagOutput.Value if destination == "" { destination = "protocol.go" } input, err := os.Open(source) handleErr(command, 1, err) defer input.Close() protocol, err := generate.ParseReader(source, input) handleErr(command, 1, err) packageName := flagPackageName.Value if packageName == "" { absDestination, err := filepath.Abs(destination) handleErr(command, 1, err) base := filepath.Base(absDestination) if scrounged, ok := scroungeForPackageName(filepath.Dir(absDestination)); ok { packageName = scrounged } else { packageName = strings.ReplaceAll( strings.ToLower(base), " ", "_") } } packageName = cleanPackageName(packageName) output, err := os.Create(destination) handleErr(command, 1, err) generator := generate.Generator { Output: output, PackageName: packageName, } _, err = generator.Generate(protocol) handleErr(command, 1, err) command.Println(destination, "OK") } func handleErr(command *cli.Cli, code int, err error) { if err != nil { command.Errorln(parse.Format(err)) os.Exit(code) } } func cleanPackageName(str string) string { buffer := []byte(str) j := 0 for _, b := range buffer { if ('a' <= b && b <= 'z') || ('A' <= b && b <= 'Z') || ('0' <= b && b <= '9') || b == '_' || b == ' ' { buffer[j] = b j++ } } return string(buffer[:j]) } func scroungeForPackageName(dir string) (string, bool) { entries, err := os.ReadDir(dir) if err != nil { return "", false} for _, entry := range entries { if !entry.Type().IsRegular() { continue } file, err := os.Open(filepath.Join(dir, entry.Name())) if err != nil { continue } defer file.Close() // FIXME: it is entirely possible that the only file will have // a shitload of doc comments preceeding the package name, and // those comments are usually huge so this is bad buffer := [512]byte { } n, _ := file.Read(buffer[:]) text := string(buffer[:n]) packageIndex := strings.Index(text, "package") if packageIndex < 0 { continue } text = text[packageIndex:] newlineIndex := strings.Index(text, "\n") if packageIndex > 0 { text = text[:newlineIndex] } fields := strings.Fields(text) if len(fields) < 2 { continue } return fields[1], true } return "", false }