Compare commits
No commits in common. "f830851e588c682ea83292cfd32dccd00f175452" and "38e36839f801b9e91ec74425310105ece7cfcb32" have entirely different histories.
f830851e58
...
38e36839f8
84
README.md
84
README.md
@ -1,84 +0,0 @@
|
|||||||
# go-pkg/cli
|
|
||||||
|
|
||||||
Lightweight, dependency-free command-line argument and option parsing library for Go.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This package provides a small, composable CLI parser with support for:
|
|
||||||
- boolean, string, int, and array/map options
|
|
||||||
- positional arguments (single and repeating)
|
|
||||||
- aliases, short flags, defaults and hidden options
|
|
||||||
- validation for incompatible options and custom "special" values
|
|
||||||
- automatic usage and version printing
|
|
||||||
|
|
||||||
See the core parser implementation in [`parser.go`](parser.go) and the high-level API in [`cli.go`](cli.go).
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Option types: [`AddBoolOpt`](opt-bool.go), [`AddStringOpt`](opt-string.go), [`AddIntOpt`](opt-int.go), [`AddStringArrayOpt`](opt-string-array.go), [`AddIntArrayOpt`](opt-int-array.go), [`AddStringMapOpt`](opt-string-map.go)
|
|
||||||
- Positional args: [`AddStringArg`](arg-string.go), [`AddStringArrayArg`](arg-string-array.go)
|
|
||||||
- Usage & version output: [`CliParser.Usage`](cli-usage.go), [`CliParser.PrintVersion`](cli-version.go)
|
|
||||||
- Option tracing via [`CliParser.TraceOptions`](cli.go) and [`SimpleOptionTracer`](simple-opt-tracer.go)
|
|
||||||
- Programmatic option setting: [`SetOptionValue`](opt-manager.go)
|
|
||||||
|
|
||||||
## Quick start
|
|
||||||
|
|
||||||
Example: define options and parse argv
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.portale-stac.it/go-pkg/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var parser cli.CliParser
|
|
||||||
parser.Init([]string{"prog", "--debug", "--config", "app.yaml", "input.txt"}, "$VER:prog,0.1.0,2025,email:$")
|
|
||||||
|
|
||||||
// define target variables and options
|
|
||||||
var debug bool
|
|
||||||
var config string
|
|
||||||
var inputs []string
|
|
||||||
|
|
||||||
parser.AddBoolOpt("debug", "d", &debug, "Enable debug")
|
|
||||||
parser.AddStringOpt("config", "c", &config, "app.yaml", "Config file")
|
|
||||||
parser.AddStringArrayArg("files", true, &inputs, "Input files")
|
|
||||||
|
|
||||||
// print usage: parser.Usage() - see [`CliParser.Usage`](cli-usage.go)
|
|
||||||
if err := parser.Parse(); err != nil {
|
|
||||||
fmt.Println("parse error:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("debug:", debug)
|
|
||||||
fmt.Println("config:", config)
|
|
||||||
fmt.Println("inputs:", inputs)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Refer to the unit test for a realistic example: [`cli_test.go`](cli_test.go).
|
|
||||||
|
|
||||||
## API Reference
|
|
||||||
|
|
||||||
Key types and methods:
|
|
||||||
- [`CliParser`](cli.go) — main parser type
|
|
||||||
- [`CliParser.Init`](cli.go) — initialize with argv and version
|
|
||||||
- [`CliParser.Parse`](cli.go) — run parsing
|
|
||||||
- Option constructors: [`AddBoolOpt`](opt-bool.go), [`AddStringOpt`](opt-string.go), [`AddIntOpt`](opt-int.go), [`AddStringArrayOpt`](opt-string-array.go), [`AddIntArrayOpt`](opt-int-array.go), [`AddStringMapOpt`](opt-string-map.go)
|
|
||||||
- Arg constructors: [`AddStringArg`](arg-string.go), [`AddStringArrayArg`](arg-string-array.go)
|
|
||||||
- Utilities: [`TraceOptions`](cli.go), [`SetOptionValue`](opt-manager.go), [`SimpleOptionTracer`](simple-opt-tracer.go)
|
|
||||||
|
|
||||||
For implementation details, consult:
|
|
||||||
- parser internals: [`parser.go`](parser.go)
|
|
||||||
- option base helpers: [`opt-base.go`](opt-base.go)
|
|
||||||
- usage/version printing: [`cli-usage.go`](cli-usage.go), [`cli-version.go`](cli-version.go)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
This project is licensed under the terms in [LICENSE](LICENSE).
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Open issues or pull requests are welcome. Run and extend the tests in [`cli_test.go`](cli_test.go) when making changes.
|
|
||||||
@ -11,12 +11,6 @@ func (cli *CliParser) Usage() string {
|
|||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
|
|
||||||
program, _ := cli.GetVersionSection("program")
|
program, _ := cli.GetVersionSection("program")
|
||||||
fmt.Fprint(&sb, "NAME ", program)
|
|
||||||
if cli.description != "" {
|
|
||||||
fmt.Fprint(&sb, " - ", cli.description)
|
|
||||||
}
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
|
|
||||||
publicCount := cli.publicOptionCount()
|
publicCount := cli.publicOptionCount()
|
||||||
if publicCount > 0 {
|
if publicCount > 0 {
|
||||||
fmt.Fprintf(&sb, "USAGE: %s [<options>] %s\n", program, cli.getArgsTemplate())
|
fmt.Fprintf(&sb, "USAGE: %s [<options>] %s\n", program, cli.getArgsTemplate())
|
||||||
|
|||||||
19
cli.go
19
cli.go
@ -2,7 +2,6 @@ package cli
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CliOptionTracer interface {
|
type CliOptionTracer interface {
|
||||||
@ -39,33 +38,17 @@ type OptReference interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CliParser struct {
|
type CliParser struct {
|
||||||
description string
|
|
||||||
version string
|
version string
|
||||||
options []cliOptionParser
|
options []cliOptionParser
|
||||||
argSpecs []argSpec
|
argSpecs []argSpec
|
||||||
cliArgs []string
|
cliArgs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *CliParser) Init(argv []string, version string, description string) {
|
func (cli *CliParser) Init(argv []string, version string) {
|
||||||
cli.version = version
|
cli.version = version
|
||||||
cli.description = description
|
|
||||||
cli.cliArgs = argv
|
cli.cliArgs = argv
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cli *CliParser) GetOption(name string) (ref OptReference) {
|
|
||||||
var opt cliOptionParser
|
|
||||||
if strings.HasPrefix(name, "-") {
|
|
||||||
opt = cli.findOptionByArg(name)
|
|
||||||
} else {
|
|
||||||
opt = cli.findOptionByName(name)
|
|
||||||
}
|
|
||||||
if opt != nil {
|
|
||||||
ref = opt.(OptReference)
|
|
||||||
}
|
|
||||||
return ref
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *CliParser) SetIncompatibleOption(optName string, incompatibleOptNames ...string) error {
|
func (cli *CliParser) SetIncompatibleOption(optName string, incompatibleOptNames ...string) error {
|
||||||
var opti cliOptionParser
|
var opti cliOptionParser
|
||||||
if opti = cli.findOptionByName(optName); opti == nil {
|
if opti = cli.findOptionByName(optName); opti == nil {
|
||||||
|
|||||||
368
cli_test.go
368
cli_test.go
@ -6,370 +6,76 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = `$VER:ddt-ocr,1.0.1,2025-11-23,celestino.amoroso@gmail.com:$`
|
type GlobaData struct {
|
||||||
|
|
||||||
type GlobalData struct {
|
|
||||||
config string
|
config string
|
||||||
log []string
|
log []string
|
||||||
printOcr bool
|
printOcr bool
|
||||||
saveClips bool
|
saveClips bool
|
||||||
trace bool
|
trace bool
|
||||||
page []int
|
page []int
|
||||||
cliVars map[string]string
|
|
||||||
inputName string
|
|
||||||
workDir string
|
|
||||||
attempts int
|
|
||||||
sources []string
|
sources []string
|
||||||
dest string
|
dest string
|
||||||
report string
|
facoltativo string
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVersion(t *testing.T) {
|
func (gd *GlobaData) String() string {
|
||||||
var cli CliParser
|
var sb strings.Builder
|
||||||
var gd GlobalData
|
fmt.Fprintf(&sb, "Options:\n")
|
||||||
|
fmt.Fprintf(&sb, " Config.....: %q\n", gd.config)
|
||||||
if err := initCli(&cli, &gd); err != nil {
|
fmt.Fprintf(&sb, " Log........: %v\n", gd.log)
|
||||||
t.Error(err)
|
fmt.Fprintf(&sb, " Print-Ocr..: %v\n", gd.printOcr)
|
||||||
return
|
fmt.Fprintf(&sb, " Save-Clips.: %v\n", gd.saveClips)
|
||||||
}
|
fmt.Fprintf(&sb, " Page.......: %v\n", gd.page)
|
||||||
if v, err := cli.GetVersionSection("all"); err == nil {
|
fmt.Fprintf(&sb, " Trace......: %v\n", gd.trace)
|
||||||
if v != version[5:len(version)-2] {
|
fmt.Fprintf(&sb, "Argumentes:\n")
|
||||||
t.Errorf("Version string does not match expected.\nGot:\n%q\nExpected:\n%q", v, version)
|
fmt.Fprintf(&sb, " Sources....: %s\n", strings.Join(gd.sources, ", "))
|
||||||
}
|
fmt.Fprintf(&sb, " Destination: %s\n", gd.dest)
|
||||||
} else {
|
fmt.Fprintf(&sb, " Facoltativo: %s\n", gd.facoltativo)
|
||||||
t.Error(err)
|
return sb.String()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestVersionNumber(t *testing.T) {
|
|
||||||
var cli CliParser
|
|
||||||
var gd GlobalData
|
|
||||||
|
|
||||||
if err := initCli(&cli, &gd); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
secValues := []string{"ddt-ocr", "1.0.1", "2025-11-23", "celestino.amoroso@gmail.com"}
|
|
||||||
for i, sec := range []string{"program", "version", "date", "email"} {
|
|
||||||
if v, err := cli.GetVersionSection(sec); err == nil {
|
|
||||||
if v != secValues[i] {
|
|
||||||
t.Errorf("Version section %q does not match expected value.\nGot:\n%q\nExpected:\n%q", sec, v, version)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsage(t *testing.T) {
|
func TestUsage(t *testing.T) {
|
||||||
const expectedUsage = `NAME ddt-ocr - cli-test
|
|
||||||
USAGE: ddt-ocr [<options>] <image-sources> ... <dest> [<report>]
|
|
||||||
where:
|
|
||||||
<image-sources> Source image files
|
|
||||||
<dest> Output destination file
|
|
||||||
<report> Optional report file
|
|
||||||
<options>
|
|
||||||
-o, --print-ocr Print the OCR output to stderr
|
|
||||||
-s, --save-clip Save the image clips as PNG files (alias: save-clips)
|
|
||||||
-t, --trace Enable trace mode for detailed logging
|
|
||||||
-p, --page(s) <num>["," ...] Process only the specified pages (comma-separated list)
|
|
||||||
-c, --config <file> Alternate configuration file
|
|
||||||
-l, --log(s) <string>["," ...] Logging options (comma-separated list)
|
|
||||||
-V, --var(s) <key=value>["," ...] Define one or more comma separated variables for the actions context (multiple allowed)
|
|
||||||
-n, --input-name <string> Input file name when source comes from stdin
|
|
||||||
-d, --work-dir <dir> Work directory
|
|
||||||
--attempts <num> Attempts for retrying failed operations (default: "1")
|
|
||||||
`
|
|
||||||
var cli CliParser
|
var cli CliParser
|
||||||
var gd GlobalData
|
var version = `$VER:mini-ddt-ocr,1.0.1,2025-11-23,celestino.amoroso@gmail.com:$`
|
||||||
|
var gd GlobaData
|
||||||
if err := initCli(&cli, &gd); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
usage := cli.Usage()
|
|
||||||
if usage != expectedUsage {
|
|
||||||
t.Errorf("Usage output does not match expected.\nGot:\n%s\nExpected:\n%s", usage, expectedUsage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestParser(t *testing.T) {
|
|
||||||
const expectedOutput = `Option: print-ocr, Type: bool, Value: true
|
|
||||||
Option: save-clip, Type: bool, Value: false
|
|
||||||
Option: trace, Type: bool, Value: true
|
|
||||||
Option: page, Type: num-array, Value: [17 18]
|
|
||||||
Option: config, Type: string, Value: devel-config.yaml
|
|
||||||
Option: log, Type: string-array, Value: [all]
|
|
||||||
Option: var, Type: map-string, Value: map[deploy_env:devel]
|
|
||||||
Option: input-name, Type: string, Value: my-scan.pdf
|
|
||||||
Option: work-dir, Type: string, Value:
|
|
||||||
Option: attempts, Type: num, Value: 1
|
|
||||||
Option: help, Type: n/a, Value: <nil>
|
|
||||||
Option: version, Type: n/a, Value: <nil>
|
|
||||||
`
|
|
||||||
|
|
||||||
var cli CliParser
|
|
||||||
var gd GlobalData
|
|
||||||
var sb strings.Builder
|
|
||||||
|
|
||||||
if err := initCli(&cli, &gd); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tracer := NewSimpleOptionTracer(&sb)
|
|
||||||
if err := cli.Parse(); err == nil {
|
|
||||||
cli.TraceOptions(tracer)
|
|
||||||
if sb.String() != expectedOutput {
|
|
||||||
t.Errorf("Parsed options do not match expected.\nGot:\n%q\nExpected:\n%q", sb.String(), expectedOutput)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCli(cli *CliParser, gd *GlobalData) (err error) {
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"ddt-ocr",
|
"mini-ddt-ocr",
|
||||||
"--log", "all",
|
"--log", "all",
|
||||||
"-t",
|
|
||||||
"--config", "devel-config.yaml",
|
"--config", "devel-config.yaml",
|
||||||
"--var", "deploy_env=devel",
|
// "--var", "deploy_env=devel",
|
||||||
"--print-ocr",
|
"--print-ocr",
|
||||||
"--pages", "17,18",
|
"--pages", "17,18",
|
||||||
"--input-name=my-scan.pdf",
|
"../../dev-stuff/ocis-upload.log", "ciccio", "bello", "pippo",
|
||||||
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
|
|
||||||
}
|
}
|
||||||
cli.Init(args, version, "cli-test")
|
cli.Init(args, version)
|
||||||
err = gd.addOptions(cli)
|
if err := gd.addOptions(&cli); err != nil {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOptErrorUnknownOption(t *testing.T) {
|
|
||||||
const unknownOption = "--logging"
|
|
||||||
var expectedErr = errUnknownOption(unknownOption)
|
|
||||||
var cli CliParser
|
|
||||||
var gd GlobalData
|
|
||||||
|
|
||||||
if err := initCliUnknownOption(&cli, &gd, unknownOption); err == nil {
|
|
||||||
if err = cli.Parse(); err != nil {
|
|
||||||
if err.Error() != expectedErr.Error() {
|
|
||||||
t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("Expected error for unknown option %q, but got none", unknownOption)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCliUnknownOption(cli *CliParser, gd *GlobalData, option string) (err error) {
|
|
||||||
args := []string{
|
|
||||||
"ddt-ocr",
|
|
||||||
option,
|
|
||||||
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
|
|
||||||
}
|
|
||||||
cli.Init(args, version, "cli-test")
|
|
||||||
err = gd.addOptions(cli)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOptErrorMissingOptionValue(t *testing.T) {
|
|
||||||
const missingValueOption = "--page"
|
|
||||||
var expectedErr = fmt.Errorf(`invalid value scan1.pdf (string) for option "page" (num-array)`)
|
|
||||||
var cli CliParser
|
|
||||||
var gd GlobalData
|
|
||||||
|
|
||||||
if err := initCliMissingOptionValue(&cli, &gd, missingValueOption); err == nil {
|
|
||||||
if err = cli.Parse(); err != nil {
|
|
||||||
if err.Error() != expectedErr.Error() {
|
|
||||||
t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("Expected error for unknown option %q, but got none", missingValueOption)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCliMissingOptionValue(cli *CliParser, gd *GlobalData, option string) (err error) {
|
|
||||||
args := []string{
|
|
||||||
"ddt-ocr",
|
|
||||||
option,
|
|
||||||
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
|
|
||||||
}
|
|
||||||
cli.Init(args, version, "cli-test")
|
|
||||||
err = gd.addOptions(cli)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOptErrorInvalidOptionValue(t *testing.T) {
|
|
||||||
const missingInvalidValueOption = "--page"
|
|
||||||
var expectedErr = errInvalidOptionValue("page", "some", "num-array")
|
|
||||||
var cli CliParser
|
|
||||||
var gd GlobalData
|
|
||||||
|
|
||||||
if err := initCliInvalidOptionValue(&cli, &gd, missingInvalidValueOption); err == nil {
|
|
||||||
if err = cli.Parse(); err != nil {
|
|
||||||
if err.Error() != expectedErr.Error() {
|
|
||||||
t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("Expected error for unknown option %q, but got none", missingInvalidValueOption)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCliInvalidOptionValue(cli *CliParser, gd *GlobalData, option string) (err error) {
|
|
||||||
args := []string{
|
|
||||||
"ddt-ocr",
|
|
||||||
option, "some",
|
|
||||||
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
|
|
||||||
}
|
|
||||||
cli.Init(args, version, "cli-test")
|
|
||||||
err = gd.addOptions(cli)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArgErrorMissingRequired(t *testing.T) {
|
|
||||||
const missingRequiredArg = "--page"
|
|
||||||
var expectedErr = fmt.Errorf(`missing required arg <image-sources>`)
|
|
||||||
var cli CliParser
|
|
||||||
var gd GlobalData
|
|
||||||
|
|
||||||
if err := initCliMissingRequiredArg(&cli, &gd); err == nil {
|
|
||||||
if err = cli.Parse(); err != nil {
|
|
||||||
if err.Error() != expectedErr.Error() {
|
|
||||||
t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("Expected error for unknown option %q, but got none", missingRequiredArg)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCliMissingRequiredArg(cli *CliParser, gd *GlobalData) (err error) {
|
|
||||||
args := []string{
|
|
||||||
"ddt-ocr",
|
|
||||||
}
|
|
||||||
cli.Init(args, version, "cli-test")
|
|
||||||
err = gd.addOptions(cli)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArgErrorReapet(t *testing.T) {
|
|
||||||
var expectedErr = fmt.Errorf(`repeat property already set for arg <other>`)
|
|
||||||
var cli CliParser
|
|
||||||
var gd GlobalData
|
|
||||||
|
|
||||||
if err := initCliReapetArg(&cli, &gd); err != nil {
|
|
||||||
if err.Error() != expectedErr.Error() {
|
|
||||||
t.Errorf("Invalid error message.\nGot:\n%v\nExpected:\n%v", err, expectedErr)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
t.Errorf("Expected error, but got none")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func initCliReapetArg(cli *CliParser, gd *GlobalData) (err error) {
|
|
||||||
args := []string{
|
|
||||||
"ddt-ocr",
|
|
||||||
"scan1.pdf", "scan2.pdf", "result.txt", "report.txt",
|
|
||||||
}
|
|
||||||
cli.Init(args, version, "cli-test")
|
|
||||||
if err = gd.addOptions(cli); err == nil {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
err = r.(error)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
cli.AddStringArrayArg("other", true, nil, "other args")
|
|
||||||
cli.AddStringArrayArg("other2", true, nil, "other args 2")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOptHidden(t *testing.T) {
|
|
||||||
const expectedUsage = `NAME ddt-ocr - cli-test
|
|
||||||
USAGE: ddt-ocr [<options>] <image-sources> ... <dest> [<report>]
|
|
||||||
where:
|
|
||||||
<image-sources> Source image files
|
|
||||||
<dest> Output destination file
|
|
||||||
<report> Optional report file
|
|
||||||
<options>
|
|
||||||
-o, --print-ocr Print the OCR output to stderr
|
|
||||||
-t, --trace Enable trace mode for detailed logging
|
|
||||||
-p, --page(s) <num>["," ...] Process only the specified pages (comma-separated list)
|
|
||||||
-c, --config <file> Alternate configuration file
|
|
||||||
-l, --log(s) <string>["," ...] Logging options (comma-separated list)
|
|
||||||
-V, --var(s) <key=value>["," ...] Define one or more comma separated variables for the actions context (multiple allowed)
|
|
||||||
-n, --input-name <string> Input file name when source comes from stdin
|
|
||||||
-d, --work-dir <dir> Work directory
|
|
||||||
--attempts <num> Attempts for retrying failed operations (default: "1")
|
|
||||||
`
|
|
||||||
var cli CliParser
|
|
||||||
var gd GlobalData
|
|
||||||
|
|
||||||
if err := initCliHiddenOpt(&cli, &gd); err != nil {
|
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
usage := cli.Usage()
|
usage := cli.Usage()
|
||||||
if usage != expectedUsage {
|
fmt.Println(usage)
|
||||||
t.Errorf("Usage output does not match expected.\nGot:\n%s\nExpected:\n%s", usage, expectedUsage)
|
if err := cli.Parse(); err == nil {
|
||||||
}
|
fmt.Println(&gd)
|
||||||
}
|
|
||||||
|
|
||||||
func initCliHiddenOpt(cli *CliParser, gd *GlobalData) (err error) {
|
|
||||||
if err = initCli(cli, gd); err == nil {
|
|
||||||
if ref := cli.GetOption("save-clip"); ref != nil {
|
|
||||||
ref.SetHidden(true)
|
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("option save-clip not found")
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gd *GlobalData) addOptions(cli *CliParser) (err error) {
|
func (gd *GlobaData) addOptions(cli *CliParser) (err error) {
|
||||||
// Always recover from panic to return error before adding options
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = r.(error)
|
err = r.(error)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
cli.AddBoolOpt("print-ocr", "o", &gd.printOcr, "Stampa l'output del programma OCR")
|
||||||
|
cli.AddBoolOpt("save-clip", "s", &gd.saveClips, "Registra le immagini delle aree ritagliata", "save-clips")
|
||||||
|
cli.AddBoolOpt("trace", "t", &gd.trace, "Attiva la modalità di tracciamento delle operazioni")
|
||||||
|
cli.AddIntArrayOpt("page", "p", &gd.page, gd.page, "Elabora la pagina specificata")
|
||||||
|
cli.AddStringOpt("config", "c", &gd.config, gd.config, "Specifica un percorso alternativo per il file di configurazione")
|
||||||
|
cli.AddStringArrayOpt("log", "l", &gd.log, gd.log, "Maschera di livelli di log")
|
||||||
|
|
||||||
// Define options
|
cli.AddStringArrayArg("sorgenti", true, &gd.sources, "file da elaborare")
|
||||||
cli.AddBoolOpt("print-ocr", "o", &gd.printOcr, "Print the OCR output to stderr")
|
cli.AddStringArg("dest", true, &gd.dest, "file di outout")
|
||||||
cli.AddBoolOpt("save-clip", "s", &gd.saveClips, "Save the image clips as PNG files", "save-clips")
|
cli.AddStringArg("facoltativo", false, &gd.facoltativo, "file facoltativo")
|
||||||
cli.AddBoolOpt("trace", "t", &gd.trace, "Enable trace mode for detailed logging")
|
|
||||||
cli.AddIntArrayOpt("page", "p", &gd.page, gd.page, "Process only the specified pages (comma-separated list)")
|
|
||||||
cli.AddFileOpt("config", "c", &gd.config, gd.config, "Alternate configuration file")
|
|
||||||
cli.AddStringArrayOpt("log", "l", &gd.log, gd.log, "Logging options (comma-separated list)")
|
|
||||||
cli.AddStringMapOpt("var", "V", &gd.cliVars, nil, "Define one or more comma separated variables for the actions context (multiple allowed)")
|
|
||||||
cli.AddStringOpt("input-name", "n", &gd.inputName, "", "Input file name when source comes from stdin")
|
|
||||||
cli.AddDirOpt("work-dir", "d", &gd.workDir, "", "Work directory")
|
|
||||||
if ref := cli.AddIntOpt("attempts", "", &gd.attempts, 1, "Attempts for retrying failed operations"); ref != nil {
|
|
||||||
ref.AddSpecialValue("many", func(manager OptManager, cliValue string, currentValue any) (any, error) {
|
|
||||||
return 1000, nil
|
|
||||||
})
|
|
||||||
ref.AddSpecialValue("few", func(manager OptManager, cliValue string, currentValue any) (any, error) {
|
|
||||||
manager.SetOptionValue("page", "1")
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define arguments
|
|
||||||
cli.AddStringArrayArg("image-sources", true, &gd.sources, "Source image files")
|
|
||||||
cli.AddStringArg("dest", true, &gd.dest, "Output destination file")
|
|
||||||
cli.AddStringArg("report", false, &gd.report, "Optional report file")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,7 +73,7 @@ func (opt *cliOptionIntArray) parse(parser cliParser, argIndex int, valuePtr *st
|
|||||||
if val, ok := boxedValue.([]int); ok {
|
if val, ok := boxedValue.([]int); ok {
|
||||||
*opt.targetVar = val
|
*opt.targetVar = val
|
||||||
} else {
|
} else {
|
||||||
err = errInvalidOptionValue(opt.name, boxedValue, "num-array")
|
err = errInvalidOptionValue(opt.name, boxedValue, "array of int")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for value := range strings.SplitSeq(optValue, ",") {
|
for value := range strings.SplitSeq(optValue, ",") {
|
||||||
@ -83,7 +83,6 @@ func (opt *cliOptionIntArray) parse(parser cliParser, argIndex int, valuePtr *st
|
|||||||
*opt.targetVar = append(*opt.targetVar, i)
|
*opt.targetVar = append(*opt.targetVar, i)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = errInvalidOptionValue(opt.name, value, "num-array")
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user