package build import ( "bytes" "os" "path" "sort" "text/template" "time" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/tarampampam/error-pages/internal/config" "github.com/tarampampam/error-pages/internal/tpl" "go.uber.org/zap" ) type historyItem struct { Code, Message, Path string } // NewCommand creates `build` command. func NewCommand(log *zap.Logger, configFile *string) *cobra.Command { //nolint:funlen,gocognit,gocyclo var ( generateIndex bool cfg *config.Config ) cmd := &cobra.Command{ Use: "build ", Aliases: []string{"b"}, Short: "Build the error pages", Args: cobra.ExactArgs(1), PreRunE: func(*cobra.Command, []string) error { if configFile == nil { return errors.New("path to the config file is required for this command") } if c, err := config.FromYamlFile(*configFile); err != nil { return err } else { if err = c.Validate(); err != nil { return err } cfg = c } return nil }, RunE: func(_ *cobra.Command, args []string) error { if len(args) != 1 { return errors.New("wrong arguments count") } errorPages := tpl.NewErrorPages() log.Info("loading templates") if templates, err := cfg.LoadTemplates(); err == nil { if len(templates) > 0 { for templateName, content := range templates { errorPages.AddTemplate(templateName, content) } for code, desc := range cfg.Pages { errorPages.AddPage(code, desc.Message, desc.Description) } } else { return errors.New("no loaded templates") } } else { return err } log.Debug("the output directory preparing", zap.String("Path", args[0])) if err := createDirectory(args[0]); err != nil { return errors.Wrap(err, "cannot prepare output directory") } history, startedAt := make(map[string][]historyItem), time.Now() log.Info("saving the error pages") if err := errorPages.IteratePages(func(template, code string, content []byte) error { if e := createDirectory(path.Join(args[0], template)); e != nil { return e } fileName := code + ".html" if e := os.WriteFile(path.Join(args[0], template, fileName), content, 0664); e != nil { //nolint:gosec,gomnd return e } if _, ok := history[template]; !ok { history[template] = make([]historyItem, 0, len(cfg.Pages)) } history[template] = append(history[template], historyItem{ Code: code, Message: cfg.Pages[code].Message, Path: path.Join(template, fileName), }) return nil }); err != nil { return err } log.Debug("saved", zap.Duration("duration", time.Since(startedAt))) if generateIndex { for _, h := range history { sort.Slice(h, func(i, j int) bool { return h[i].Code < h[j].Code }) } log.Info("index file generation") startedAt = time.Now() if err := writeIndexFile(path.Join(args[0], "index.html"), history); err != nil { return err } log.Debug("index file generated", zap.Duration("duration", time.Since(startedAt))) } return nil }, } cmd.Flags().BoolVarP( &generateIndex, "index", "i", false, "generate index page", ) return cmd } func createDirectory(path string) error { stat, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { return os.MkdirAll(path, 0775) //nolint:gomnd } return err } if !stat.IsDir() { return errors.New("is not a directory") } return nil } func writeIndexFile(path string, history map[string][]historyItem) error { t, err := template.New("index").Parse(` Error pages list

Error pages index

{{- range $template, $item := . -}}

Template name: {{ $template }}

{{ end }}
`) if err != nil { return err } var buf bytes.Buffer if err = t.Execute(&buf, history); err != nil { return err } return os.WriteFile(path, buf.Bytes(), 0664) //nolint:gosec,gomnd }