chore: prepare docfill project for gitea
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"xlsdoc/internal/docfill"
|
||||
@@ -18,11 +20,11 @@ func main() {
|
||||
|
||||
func run(args []string, stdout, stderr io.Writer) int {
|
||||
if len(args) == 0 {
|
||||
fmt.Fprintln(stderr, "docfill: missing command: run")
|
||||
writeCodedError(stderr, docfill.CodeCLIUsage, "missing command: run", "Use: docfill run -c task.json", nil)
|
||||
return 2
|
||||
}
|
||||
if args[0] != "run" {
|
||||
fmt.Fprintf(stderr, "docfill: unknown command: %s\n", args[0])
|
||||
writeCodedError(stderr, docfill.CodeCLIUsage, "unknown command: "+args[0], "Use: docfill run -c task.json", nil)
|
||||
return 2
|
||||
}
|
||||
|
||||
@@ -43,38 +45,78 @@ func runFill(args []string, stderr io.Writer) (docfill.RunResult, int) {
|
||||
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 {
|
||||
fmt.Fprintf(stderr, "docfill: %v\n", err)
|
||||
writeCodedError(stderr, docfill.CodeCLIUsage, err.Error(), "Check command flags and use key=value for --var.", nil)
|
||||
return docfill.RunResult{}, 2
|
||||
}
|
||||
if fs.NArg() > 0 {
|
||||
fmt.Fprintf(stderr, "docfill: unexpected argument: %s\n", fs.Arg(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 == "" {
|
||||
fmt.Fprintln(stderr, "docfill: missing --config")
|
||||
writeCodedError(stderr, docfill.CodeCLIUsage, "missing --config", "Use -c task.json or --config task.json.", nil)
|
||||
fs.SetOutput(stderr)
|
||||
fs.Usage()
|
||||
return docfill.RunResult{}, 2
|
||||
}
|
||||
|
||||
result, err := docfill.RunWithOptions(configPath, docfill.RunOptions{Vars: vars})
|
||||
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 {
|
||||
fmt.Fprintf(stderr, "docfill: %v\n", err)
|
||||
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 ""
|
||||
|
||||
@@ -63,7 +63,7 @@ func TestRunCommandRejectsBadVarSyntax(t *testing.T) {
|
||||
if exitCode != 2 {
|
||||
t.Fatalf("exit code = %d, want 2", exitCode)
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "docfill:") || !strings.Contains(stderr.String(), "invalid --var") {
|
||||
if !strings.Contains(stderr.String(), "[CLI_USAGE]") || !strings.Contains(stderr.String(), "invalid --var") {
|
||||
t.Fatalf("unexpected stderr: %q", stderr.String())
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,96 @@ func TestRunCommandRejectsUnexpectedArgs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandFormatsCodedExecutionErrors(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
dataPath := filepath.Join(dir, "data.xlsx")
|
||||
templatePath := filepath.Join(dir, "template.xlsx")
|
||||
configPath := filepath.Join(dir, "task.json")
|
||||
|
||||
writeCLIWorkbook(t, dataPath, "Daily", [][]string{
|
||||
{"name"},
|
||||
{"张三"},
|
||||
})
|
||||
writeCLIWorkbook(t, templatePath, "日报", [][]string{
|
||||
{"{{name}}", "{{missing}}"},
|
||||
})
|
||||
writeCLIJSON(t, configPath, map[string]any{
|
||||
"data": map[string]any{
|
||||
"type": "excel",
|
||||
"path": dataPath,
|
||||
"sheet": "Daily",
|
||||
},
|
||||
"template": map[string]any{
|
||||
"path": templatePath,
|
||||
},
|
||||
"output": map[string]any{
|
||||
"path": filepath.Join(dir, "out", "日报_{{name}}.xlsx"),
|
||||
},
|
||||
})
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
exitCode := run([]string{"run", "-c", configPath}, &stdout, &stderr)
|
||||
|
||||
if exitCode != 1 {
|
||||
t.Fatalf("exit code = %d, want 1", exitCode)
|
||||
}
|
||||
if stdout.Len() != 0 {
|
||||
t.Fatalf("expected empty stdout, got %q", stdout.String())
|
||||
}
|
||||
got := stderr.String()
|
||||
for _, want := range []string{"docfill: [TEMPLATE_FIELD_MISSING]", "hint:", "context:", "field=missing", "template="} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Fatalf("stderr missing %q: %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCommandVerbosePrintsStepLogs(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
dataPath := filepath.Join(dir, "data.xlsx")
|
||||
templatePath := filepath.Join(dir, "template.xlsx")
|
||||
configPath := filepath.Join(dir, "task.json")
|
||||
|
||||
writeCLIWorkbook(t, dataPath, "Daily", [][]string{
|
||||
{"name", "done"},
|
||||
{"张三", "完成日报"},
|
||||
})
|
||||
writeCLIWorkbook(t, templatePath, "日报", [][]string{
|
||||
{"{{name}}", "{{done}}"},
|
||||
})
|
||||
writeCLIJSON(t, configPath, map[string]any{
|
||||
"data": map[string]any{
|
||||
"type": "excel",
|
||||
"path": dataPath,
|
||||
"sheet": "Daily",
|
||||
},
|
||||
"template": map[string]any{
|
||||
"path": templatePath,
|
||||
},
|
||||
"output": map[string]any{
|
||||
"path": filepath.Join(dir, "out", "日报_{{name}}.xlsx"),
|
||||
},
|
||||
})
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
exitCode := run([]string{"run", "-c", configPath, "--verbose"}, &stdout, &stderr)
|
||||
|
||||
if exitCode != 0 {
|
||||
t.Fatalf("exit code = %d, stderr=%q", exitCode, stderr.String())
|
||||
}
|
||||
if !strings.Contains(stdout.String(), "generated 1 file(s)") {
|
||||
t.Fatalf("unexpected stdout: %q", stdout.String())
|
||||
}
|
||||
got := stderr.String()
|
||||
for _, want := range []string{"log: load config", "log: read data", "log: render row=1"} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Fatalf("stderr missing %q: %q", want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeCLIWorkbook(t *testing.T, path, sheet string, rows [][]string) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user