package main import ( "embed" "flag" "fmt" "io" "log" "os" "os/exec" "path/filepath" "text/template" "time" "codeflow.dananglin.me.uk/apollo/spruce/internal/cv" tf "codeflow.dananglin.me.uk/apollo/spruce/internal/templateFuncs" ) //go:embed templates/tex/* var templates embed.FS func main() { var input, output string var employmentHistory int 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.IntVar(&employmentHistory, "employment-history", 10, "show employment history within these number of years.") flag.Parse() if err := run(input, output, employmentHistory); err != nil { log.Fatalf("ERROR: %v", err) } } func run(input, output string, employmentHistory int) error { historyLimit := time.Now().AddDate(-1*employmentHistory, 0, 0) tempDir, err := os.MkdirTemp("/tmp", "cv-builder-") if err != nil { return fmt.Errorf("unable to create a temporary directory; %w", 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, historyLimit) if err != nil { return fmt.Errorf("unable to create the tex file; %w", err) } if err := pdf(tempDir, texFile, output); err != nil { return fmt.Errorf("unable to create the PDF file; %w", err) } return nil } // tex generates the CV document as a Tex file. func tex(input, tempDir string, historyLimit time.Time) (string, error) { c, err := cv.NewCV(input) if err != nil { return "", fmt.Errorf("unable to create a new CV value from %s; %w", input, 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": tf.NotLastElement, "join": tf.JoinSentences, "durationToString": tf.FormatDuration, "withinTimePeriod": tf.WithinTimePeriod(historyLimit), } t := template.Must(template.New("cv.tmpl.tex"). Funcs(fmap). Delims("<<", ">>"). ParseFS(templates, "templates/tex/*.tmpl.tex"), ) if err = t.Execute(file, c); 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 }