139 lines
3.6 KiB
Go
139 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
|
|
"xlsdoc/internal/docfill"
|
|
)
|
|
|
|
type varFlags map[string]string
|
|
|
|
func main() {
|
|
os.Exit(run(os.Args[1:], os.Stdout, os.Stderr))
|
|
}
|
|
|
|
func run(args []string, stdout, stderr io.Writer) int {
|
|
if len(args) == 0 {
|
|
writeCodedError(stderr, docfill.CodeCLIUsage, "missing command: run", "Use: docfill run -c task.json", nil)
|
|
return 2
|
|
}
|
|
if args[0] != "run" {
|
|
writeCodedError(stderr, docfill.CodeCLIUsage, "unknown command: "+args[0], "Use: docfill run -c task.json", nil)
|
|
return 2
|
|
}
|
|
|
|
result, code := runFill(args[1:], stderr)
|
|
if code != 0 {
|
|
return code
|
|
}
|
|
|
|
fmt.Fprintf(stdout, "generated %d file(s)\n", len(result.Files))
|
|
for _, file := range result.Files {
|
|
fmt.Fprintf(stdout, "- %s\n", file)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func runFill(args []string, stderr io.Writer) (docfill.RunResult, int) {
|
|
fs := flag.NewFlagSet("docfill run", flag.ContinueOnError)
|
|
fs.SetOutput(stderr)
|
|
|
|
var configPath string
|
|
var verbose bool
|
|
var vars varFlags = make(map[string]string)
|
|
fs.SetOutput(io.Discard)
|
|
fs.StringVar(&configPath, "config", "", "path to task json")
|
|
fs.StringVar(&configPath, "c", "", "path to task json")
|
|
fs.BoolVar(&verbose, "verbose", false, "print diagnostic logs to stderr")
|
|
fs.Var(vars, "var", "template variable as key=value; repeatable")
|
|
|
|
if err := fs.Parse(args); err != nil {
|
|
writeCodedError(stderr, docfill.CodeCLIUsage, err.Error(), "Check command flags and use key=value for --var.", nil)
|
|
return docfill.RunResult{}, 2
|
|
}
|
|
if fs.NArg() > 0 {
|
|
writeCodedError(stderr, docfill.CodeCLIUsage, "unexpected argument: "+fs.Arg(0), "Remove extra positional arguments.", nil)
|
|
return docfill.RunResult{}, 2
|
|
}
|
|
if vars == nil {
|
|
vars = make(map[string]string)
|
|
}
|
|
if configPath == "" {
|
|
writeCodedError(stderr, docfill.CodeCLIUsage, "missing --config", "Use -c task.json or --config task.json.", nil)
|
|
fs.SetOutput(stderr)
|
|
fs.Usage()
|
|
return docfill.RunResult{}, 2
|
|
}
|
|
|
|
var logf func(format string, args ...any)
|
|
if verbose {
|
|
logf = func(format string, args ...any) {
|
|
fmt.Fprintf(stderr, "log: "+format+"\n", args...)
|
|
}
|
|
}
|
|
|
|
result, err := docfill.RunWithOptions(configPath, docfill.RunOptions{Vars: vars, Logf: logf})
|
|
if err != nil {
|
|
writeExecutionError(stderr, err)
|
|
return docfill.RunResult{}, 1
|
|
}
|
|
return result, 0
|
|
}
|
|
|
|
func writeExecutionError(stderr io.Writer, err error) {
|
|
var appErr *docfill.AppError
|
|
if errors.As(err, &appErr) {
|
|
writeCodedError(stderr, appErr.Code, err.Error(), appErr.Hint, appErr.Context)
|
|
return
|
|
}
|
|
writeCodedError(stderr, docfill.CodeInternal, err.Error(), "Run with --verbose and inspect inputs if this repeats.", nil)
|
|
}
|
|
|
|
func writeCodedError(stderr io.Writer, code docfill.ErrorCode, message, hint string, context map[string]string) {
|
|
fmt.Fprintf(stderr, "docfill: [%s] %s\n", code, message)
|
|
if hint != "" {
|
|
fmt.Fprintf(stderr, "hint: %s\n", hint)
|
|
}
|
|
if len(context) == 0 {
|
|
return
|
|
}
|
|
|
|
keys := make([]string, 0, len(context))
|
|
for key := range context {
|
|
keys = append(keys, key)
|
|
}
|
|
sort.Strings(keys)
|
|
|
|
parts := make([]string, 0, len(keys))
|
|
for _, key := range keys {
|
|
parts = append(parts, key+"="+context[key])
|
|
}
|
|
fmt.Fprintf(stderr, "context: %s\n", strings.Join(parts, " "))
|
|
}
|
|
|
|
func (v varFlags) String() string {
|
|
if len(v) == 0 {
|
|
return ""
|
|
}
|
|
parts := make([]string, 0, len(v))
|
|
for key, value := range v {
|
|
parts = append(parts, key+"="+value)
|
|
}
|
|
return strings.Join(parts, ",")
|
|
}
|
|
|
|
func (v varFlags) Set(value string) error {
|
|
key, val, ok := strings.Cut(value, "=")
|
|
if !ok || strings.TrimSpace(key) == "" {
|
|
return fmt.Errorf("invalid --var %q, want key=value", value)
|
|
}
|
|
v[strings.TrimSpace(key)] = val
|
|
return nil
|
|
}
|