107 lines
3.0 KiB
Go
107 lines
3.0 KiB
Go
package cli
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"go/token"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"unicode"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func newGenerateCmd(opts *options) *cobra.Command {
|
|
var (
|
|
out string
|
|
pkg string
|
|
check bool
|
|
excludeFieldDoc bool
|
|
)
|
|
cmd := &cobra.Command{
|
|
Use: "generate",
|
|
Short: "Generate Go types from the config",
|
|
Long: `Generate fetches the schemas and extensions named in the config and writes
|
|
the Go types to --out. Use --out - to write to stdout.
|
|
|
|
With --check, nothing is written; the command fails if the output file is
|
|
missing or differs from what would be generated. Use this in CI to catch
|
|
generated code that is stale or was edited by hand.`,
|
|
Example: ` ocdsgen generate -c extensions.json -o ocds_gen.go -p tenders
|
|
ocdsgen generate --check --strict
|
|
//go:generate go tool ocdsgen generate -c extensions.json -o ocds_gen.go -p tenders`,
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
if pkg == "" {
|
|
var err error
|
|
if pkg, err = packageFromPath(out); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
cfg, g, err := opts.setup(cmd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
src, err := g.Run(cfg, pkg, filepath.Base(opts.config), excludeFieldDoc)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch {
|
|
case out == "-":
|
|
if _, err := cmd.OutOrStdout().Write(src); err != nil {
|
|
return err
|
|
}
|
|
case check:
|
|
existing, err := os.ReadFile(out)
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return fmt.Errorf("%s does not exist; run ocdsgen generate", out)
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
if !bytes.Equal(existing, src) {
|
|
return fmt.Errorf("%s is out of date; run ocdsgen generate", out)
|
|
}
|
|
fmt.Fprintf(cmd.ErrOrStderr(), "%s is up to date\n", out)
|
|
default:
|
|
if err := os.WriteFile(out, src, 0o644); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return opts.finish(g)
|
|
},
|
|
}
|
|
|
|
f := cmd.Flags()
|
|
f.StringVarP(&out, "out", "o", "ocds_gen.go", `output Go file, or "-" for stdout`)
|
|
f.StringVarP(&pkg, "package", "p", "", "Go package name (default: name of the output file's directory)")
|
|
f.BoolVar(&check, "check", false, "verify the output file is up to date instead of writing it")
|
|
f.BoolVar(&excludeFieldDoc, "exclude-field-doc", false, `exclude documentation for definition fields`)
|
|
return cmd
|
|
}
|
|
|
|
// packageFromPath derives a package name from the directory the output file
|
|
// will be written to, e.g. internal/tenders/ocds_gen.go -> tenders.
|
|
func packageFromPath(out string) (string, error) {
|
|
if out == "-" {
|
|
return "", errors.New("--package is required when writing to stdout")
|
|
}
|
|
abs, err := filepath.Abs(out)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
dir := filepath.Base(filepath.Dir(abs))
|
|
name := strings.Map(func(r rune) rune {
|
|
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
|
|
return unicode.ToLower(r)
|
|
}
|
|
return -1
|
|
}, dir)
|
|
if !token.IsIdentifier(name) || token.IsKeyword(name) {
|
|
return "", fmt.Errorf("can't derive a package name from directory %q; pass --package", dir)
|
|
}
|
|
return name, nil
|
|
}
|