Шаблон Golang пуст, но ошибок не возникает

Я работал над тем, чтобы README автоматически генерировались для некоторых программ Cobra CLI. Поначалу все шло хорошо, и я получил первый README, который сгенерировался нормально. Однако по какой-то причине вторая попытка с использованием той же функции не удалась. Итак, после нескольких попыток заставить его работать, я решил задать вопрос здесь.

Весь код можно найти здесь с помощью Makefile (makegenerate — это команда для создания файлов README). По какой-то причине шаблон, который не генерируется, отображается как пустая строка.

Тот же код используется для генерируемого файла README, который представляет собой просто пустую строку. Вот общий код:

package cmdhandler

import (
    "bufio"
    "bytes"
    "errors"
    "fmt"
    "strings"
    "text/template"

    "github.com/MakeNowJust/heredoc"
    "github.com/davecgh/go-spew/spew"
    cmdtomd "github.com/pjkaufman/go-go-gadgets/pkg/cmd-to-md"
    filehandler "github.com/pjkaufman/go-go-gadgets/pkg/file-handler"
    "github.com/pjkaufman/go-go-gadgets/pkg/logger"
    "github.com/spf13/cobra"
)

var generationDir string

const GenerationDirArgEmpty = "generation-dir must have a non-whitespace value"

type TmplData struct {
    CommandStrings string
    Todos          []string
    Description    string
    Title          string
    CustomValues   map[string]any
}

func AddGenerateCmd(rootCmd *cobra.Command, title, description string, todos []string, getCustomValues func(string) (map[string]any, error)) {
    var generateCmd = &cobra.Command{
        Use:   "generate",
        Short: "Generates the readme file for the program",
        Example: heredoc.Doc(`
            ` + rootCmd.Use + ` generate -d ./` + rootCmd.Use + `
            will look for a file called README.md.tmpl and if it is found generate a readme based
            on that file and the file command info.
        `),
        Run: func(cmd *cobra.Command, args []string) {
            err := ValidateGenerateFlags(generationDir)
            if err != nil {
                logger.WriteError(err.Error())
            }

            err = filehandler.FolderMustExist(generationDir, "generation-dir")
            if err != nil {
                logger.WriteError(err.Error())
            }

            tmpl, err := template.ParseFiles(filehandler.JoinPath(generationDir, "README.md.tmpl"))
            if err != nil {
                logger.WriteError(err.Error())
            }

            var customValues = make(map[string]any)
            if getCustomValues != nil {
                customValues, err = getCustomValues(generationDir)

                if err != nil {
                    logger.WriteError(err.Error())
                }
            }

            var b bytes.Buffer
            writer := bufio.NewWriter(&b)

            err = tmpl.Execute(writer, TmplData{
                CommandStrings: cmdtomd.RootToMd(rootCmd),
                Todos:          todos,
                Description:    description,
                Title:          title,
                CustomValues:   customValues,
            })
            if err != nil {
                logger.WriteError(err.Error())
            }

            spew.Dump(TmplData{
                CommandStrings: cmdtomd.RootToMd(rootCmd),
                Todos:          todos,
                Description:    description,
                Title:          title,
                CustomValues:   customValues,
            })

            err = filehandler.WriteFileContents(filehandler.JoinPath(generationDir, "README.md"), b.String())
            if err != nil {
                logger.WriteError(err.Error())
            }
        },
    }

    rootCmd.AddCommand(generateCmd)

    generateCmd.Flags().StringVarP(&generationDir, "generation-dir", "g", "", "the path to the base folder of the "+rootCmd.Use+" program source code")
    err := generateCmd.MarkFlagRequired("generation-dir")
    if err != nil {
        logger.WriteError(fmt.Sprintf(`failed to mark flag "generation-dir" as required on generate command: %v`, err))
    }

    // keep from showing up in the output of the command generation
    generateCmd.Hidden = true
}

func ValidateGenerateFlags(generationDir string) error {
    if strings.TrimSpace(generationDir) == "" {
        return errors.New(GenerationDirArgEmpty)
    }

    return nil
}

Код, который использует эту логику и работает неправильно:

//go:build generate

package cmd

import (
    cmdhandler "github.com/pjkaufman/go-go-gadgets/pkg/cmd-handler"
)

const (
    title       = "Jpeg and Png Processor"
    description = `This is meant to be a replacement for my usage of imgp.

Currently I use imgp for the following things:
- image resizing
- exif data removal
- image quality setting

Given how this works, I find it easier to just go ahead and do a simple program in Go to see how things stack up and not be so reliant on Python. This also helps me learn some more about imaging processing as well. So a win-win in my book.`
)

func init() {
    cmdhandler.AddGenerateCmd(rootCmd, title, description, []string{
        "Resize png test",
    }, nil)
}

Переданный шаблон:

<!-- This file is generated from  https://github.com/pjkaufman/go-go-gadgets/jp-proc/README.md.tmpl. Please make any necessary changes there. -->

# {{ .Title }}

{{ .Description }}

## How does this program compare with imgp?

| Operation | Original Size | New Size (imgp) | New Size (imgp with optimize flag) | New Size (jp-proc) |
| --------- | ------------- | --------------- | ---------------------------------- | ------------------ |
| Resize jpeg to 800x600 and remove exif data | 3.4M | 57KB | 56KB | 68KB |
| Resize jpeg to 800x600 and remove exif data and set quality to 40 | 3.4M | 32KB | 28KB | 37KB |
{{- if .Todos }}

## TODOs

{{- range .Todos }}
- {{ . }}
{{- end}}
{{- end}}

## Commands

{{ .CommandStrings }}

Я создал образец программы с тем же шаблоном, и он работает, но по какой-то причине я не могу заставить работать другую настройку. Вот работающая автономная программа:

package main

import (
    "log"
    "os"
    "text/template"
)

const format = `# {{ .Title }}

{{ .Description }}

## How does this program compare with imgp?

| Operation | Original Size | New Size (imgp) | New Size (imgp with optimize flag) | New Size (jp-proc) |
| --------- | ------------- | --------------- | ---------------------------------- | ------------------ |
| Resize jpeg to 800x600 and remove exif data | 3.4M | 57KB | 56KB | 68KB |
| Resize jpeg to 800x600 and remove exif data and set quality to 40 | 3.4M | 32KB | 28KB | 37KB |
{{- if .Todos }}

## TODOs

{{- range .Todos }}
- {{ . }}
{{- end}}
{{- end}}

## Commands

{{ .CommandStrings }}
`

type TmplData struct {
    CommandStrings string
    Todos          []string
    Description    string
    Title          string
    CustomValues   map[string]any
}

func main() {
    test := template.New("testTemp")

    tmpl, err := test.Parse(format)
    if err != nil {
        log.Fatal(err)
    }

    err = tmpl.ExecuteTemplate(os.Stdout, "testTemp", TmplData{
        Title:       "Test Title",
        Description: "This is a test template",
        Todos: []string{
            "TODO 1",
            "TODO 2",
        },
        CommandStrings: "Some command string text here...",
    })
    if err != nil {
        log.Fatal(err)
    }
}

Есть идеи, почему шаблон будет работать в автономной программе, но не в командах кобры?

Пожалуйста, дайте мне знать, если какая-либо другая информация будет полезна. Спасибо!

Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
0
0
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Смытьбуфио.Писатель:

var b bytes.Buffer
writer := bufio.NewWriter(&b)

err = tmpl.Execute(writer, TmplData{
    CommandStrings: cmdtomd.RootToMd(rootCmd),
    Todos:          todos,
    Description:    description,
    Title:          title,
    CustomValues:   customValues,
})
if err != nil {
    logger.WriteError(err.Error())
}
writer.Flush() // <--- add this line

Лучшее решение — напрямую записать в bytes.Buffer:

var b bytes.Buffer 
err = tmpl.Execute(&b, TmplData{ // <--- write to buffer directly
    CommandStrings: cmdtomd.RootToMd(rootCmd),
    Todos:          todos,
    Description:    description,
    Title:          title,
    CustomValues:   customValues,
})
if err != nil {
    logger.WriteError(err.Error())
}

Спасибо @Eaten Scribner. Это работает! Я думал, что он не будет записывать данные напрямую в буфер, но, похоже, я ошибался.

Peter Kaufman 14.07.2024 02:41

Другие вопросы по теме