package main import ( "embed" "encoding/json" "flag" "fmt" "log" "os" "os/exec" "path/filepath" "text/template" "io" ) //go:embed templates/tex/* var templates embed.FS func main() { var input, output string flag.StringVar(&input, "input", "", "specify the CV JSON file that you want to input to the builder.") flag.StringVar(&output, "output", "", "specify the name of the output CV file.") flag.Parse() tempDir, err := os.MkdirTemp("/tmp", "cv-builder-") if err != nil { log.Fatalf("ERROR: Unable to create a temporary directory; %v", err) } defer func() { err := os.RemoveAll(tempDir) if err != nil { log.Printf("WARN: An error occurred when removing the temporary directory; %v", err) } }() texFile, err := tex(input, tempDir) if err != nil { log.Fatalf("ERROR: %v", err) } if err := pdf(tempDir, texFile, output); err != nil { log.Fatalf("ERROR: %v", err) } } // tex generates the CV document as a Tex file. func tex(input, tempDir string) (string, error) { data, err := os.ReadFile(input) if err != nil { return "", fmt.Errorf("unable to read data from file; %w", err) } var cv Cv if err = json.Unmarshal(data, &cv); err != nil { return "", fmt.Errorf("unable to decode JSON data; %w", err) } output := filepath.Join(tempDir, "cv.tex") file, err := os.Create(output) if err != nil { return "", fmt.Errorf("unable to create output file %s; %w", output, err) } defer file.Close() fmap := template.FuncMap{ "notLastElement": notLastElement, "join": join, "durationToString": durationToString, } t := template.Must(template.New("cv.tmpl.tex"). Funcs(fmap). Delims("<<", ">>"). ParseFS(templates, "templates/tex/*.tmpl.tex"), ) if err = t.Execute(file, cv); err != nil { return "", fmt.Errorf("unable to execute the CV template. %w", err) } log.Printf("INFO: Tex file %s was successfully created.", output) return output, nil } // pdf generates the CV document as a PDF file from the tex file. func pdf(tempDir, texFile, output string) error { pathArg := "--path=" + tempDir command := exec.Command("mtxrun", pathArg, "--script", "context", texFile) command.Stderr = os.Stderr command.Stdout = os.Stdout if err := command.Run(); err != nil { return err } if output == "" { output = "./cv.pdf" } if err := copy(filepath.Join(tempDir, "cv.pdf"), output); err != nil { return err } return nil } func copy(input, output string) error { inputFile, err := os.Open(input) if err != nil { return fmt.Errorf("unable to open %s; %w", input, err) } defer inputFile.Close() outputFile, err := os.Create(output) if err != nil { return fmt.Errorf("unable to create %s; %w", output, err) } defer outputFile.Close() _, err = io.Copy(outputFile, inputFile) if err != nil { return fmt.Errorf("unable to copy %s to %s; %w", input, output, err) } return nil }