spruce/internal/cmd/generate.go
Dan Anglin 4f90a4c8bb
fix: use default FlagSet for spruce help message
Use the default FlagSet to parse all the arguments and to set the
default help message for spruce. Arguments set after the subcommand are
still parsed by the subcommand's FlagSet.

The summaries for all subcommand are defined in one place in the main
function for consistency.

The internal/cmd.SpruceUsage function is replaced with the
spruceUsageFunc function in the main package which returns the usage
function which is set as the default usage function.

The format of the help message for spruce and the subcommands have been
updated with the inspiration of the help message from gopass.
2023-08-20 06:14:10 +01:00

99 lines
2.5 KiB
Go

package cmd
import (
"flag"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"time"
"codeflow.dananglin.me.uk/apollo/spruce/internal/pdf"
)
type GenerateCommand struct {
*flag.FlagSet
summary string
input string
output string
employmentHistory int
verbose bool
}
type noInputSpecifiedError struct{}
func (e noInputSpecifiedError) Error() string {
return "no input file specified, please set the --input field"
}
func NewGenerateCommand(name, summary string) *GenerateCommand {
command := GenerateCommand{
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
summary: summary,
}
command.StringVar(&command.input, "input", "", "specify the CV JSON file that you want to input to the builder.")
command.StringVar(&command.output, "output", "cv.pdf", "specify the name of the output CV file.")
command.IntVar(&command.employmentHistory, "employment-history", 10, "show employment history within these number of years.")
command.BoolVar(&command.verbose, "verbose", false, "set to true to enable verbose logging.")
command.Usage = usageFunc(command.Name(), command.summary, command.FlagSet)
return &command
}
func (c *GenerateCommand) Run() error {
if c.input == "" {
return noInputSpecifiedError{}
}
historyLimit := time.Now().AddDate(-1*c.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 {
slog.Warn(fmt.Sprintf("WARN: An error occurred when removing the temporary directory; %v", err))
}
}()
pdfFileName, err := pdf.Generate(tempDir, c.input, historyLimit, c.verbose)
if err != nil {
return fmt.Errorf("unable to create the PDF file; %w", err)
}
if err := copyfile(filepath.Join(tempDir, "cv.pdf"), c.output); err != nil {
return fmt.Errorf("unable to copy %s to %s; %w", pdfFileName, c.output, err)
}
return nil
}
func copyfile(source, destination string) error {
inputFile, err := os.Open(source)
if err != nil {
return fmt.Errorf("unable to open %s; %w", source, err)
}
defer inputFile.Close()
outputFile, err := os.Create(destination)
if err != nil {
return fmt.Errorf("unable to create %s; %w", destination, err)
}
defer outputFile.Close()
_, err = io.Copy(outputFile, inputFile)
if err != nil {
return fmt.Errorf("unable to copy %s to %s; %w", source, destination, err)
}
slog.Info("File successfully copied.", "source", source, "destination", destination)
return nil
}