Use flaggy for CLI argument parsing
This commit is contained in:
@@ -2,13 +2,14 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/integrii/flaggy"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
@@ -38,80 +39,84 @@ type DocspellItem struct {
|
||||
}
|
||||
|
||||
func main() {
|
||||
parser, commands := newCLIParser()
|
||||
if len(os.Args) < 2 {
|
||||
printUsage(os.Stderr)
|
||||
parser.ShowHelp()
|
||||
return
|
||||
}
|
||||
if err := parser.ParseArgs(os.Args[1:]); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "import":
|
||||
if isHelpRequest(os.Args[2:]) {
|
||||
runImportCommand(config{}, "", os.Args[2:])
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case commands.importCommand.Used:
|
||||
cfg, password := loadConfigAndPassword()
|
||||
runImportCommand(cfg, password, os.Args[2:])
|
||||
case "mount":
|
||||
if isHelpRequest(os.Args[2:]) {
|
||||
_ = runMountCommand(os.Args[2:], config{}, "")
|
||||
return
|
||||
}
|
||||
case commands.mountCommand.Used:
|
||||
cfg, password := loadConfigAndPassword()
|
||||
if err := runMountCommand(os.Args[2:], cfg, password); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "mount failed:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "get-path":
|
||||
if isHelpRequest(os.Args[2:]) {
|
||||
_ = runGetPathCommand(os.Args[2:], config{}, "")
|
||||
return
|
||||
}
|
||||
case commands.getPathCommand.Used:
|
||||
cfg, password := loadConfigAndPassword()
|
||||
if err := runGetPathCommand(os.Args[2:], cfg, password); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "get-path failed:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "get-url":
|
||||
if isHelpRequest(os.Args[2:]) {
|
||||
_ = runGetURLCommand(os.Args[2:], config{}, "")
|
||||
return
|
||||
}
|
||||
case commands.getURLCommand.Used:
|
||||
cfg, password := loadConfigAndPassword()
|
||||
if err := runGetURLCommand(os.Args[2:], cfg, password); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "get-url failed:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "-h", "--help", "help":
|
||||
printUsage(os.Stdout)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "unknown command %q\n\n", os.Args[1])
|
||||
printUsage(os.Stderr)
|
||||
os.Exit(2)
|
||||
parser.ShowHelp()
|
||||
}
|
||||
}
|
||||
|
||||
func isHelpRequest(args []string) bool {
|
||||
for _, arg := range args {
|
||||
if arg == "-h" || arg == "--help" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
type cliCommands struct {
|
||||
importCommand *flaggy.Subcommand
|
||||
mountCommand *flaggy.Subcommand
|
||||
getPathCommand *flaggy.Subcommand
|
||||
getURLCommand *flaggy.Subcommand
|
||||
}
|
||||
|
||||
func printUsage(out *os.File) {
|
||||
name := filepath.Base(os.Args[0])
|
||||
fmt.Fprintf(out, `Usage:
|
||||
%[1]s <command> [flags]
|
||||
func newCLIParser() (*flaggy.Parser, cliCommands) {
|
||||
parser := flaggy.NewParser(filepath.Base(os.Args[0]))
|
||||
parser.Description = "Docspell import, archive, mount, and helper CLI"
|
||||
parser.ShowHelpOnUnexpected = true
|
||||
parser.DisableShowVersionWithVersion()
|
||||
|
||||
Commands:
|
||||
import Scan configured import folders, archive confirmed files, and optionally upload missing files
|
||||
mount Expose Docspell search results as a read-only FUSE filesystem
|
||||
get-path Resolve a Docspell item ID or URL to the local mounted path
|
||||
get-url Resolve a local mounted path, item ID, or item URL to a Docspell web URL
|
||||
commands := cliCommands{
|
||||
importCommand: flaggy.NewSubcommand("import"),
|
||||
mountCommand: flaggy.NewSubcommand("mount"),
|
||||
getPathCommand: flaggy.NewSubcommand("get-path"),
|
||||
getURLCommand: flaggy.NewSubcommand("get-url"),
|
||||
}
|
||||
commands.importCommand.Description = "Scan configured import folders, archive confirmed files, and optionally upload missing files"
|
||||
commands.mountCommand.Description = "Expose Docspell search results as a read-only FUSE filesystem"
|
||||
commands.getPathCommand.Description = "Resolve a Docspell item ID or URL to the local mounted path"
|
||||
commands.getURLCommand.Description = "Resolve a local mounted path, item ID, or item URL to a Docspell web URL"
|
||||
|
||||
Run "%[1]s <command> -h" for command-specific help.
|
||||
`, name)
|
||||
var uploadMissing bool
|
||||
commands.importCommand.Bool(&uploadMissing, "", "upload-missing", "true, to upload files to docspell that do not yet exist there")
|
||||
var query, mountPoint, itemIDOrURL, pathOrID string
|
||||
authHeader := defaultDocspellAuthHeader
|
||||
limit := 1000
|
||||
commands.mountCommand.String(&query, "", "query", "Docspell item query to expose")
|
||||
commands.mountCommand.Int(&limit, "", "limit", "number of items to fetch per API request")
|
||||
commands.mountCommand.String(&authHeader, "", "auth-header", "HTTP header used for the Docspell auth token")
|
||||
commands.mountCommand.AddPositionalValue(&mountPoint, "mountpoint", 1, false, "mount point, overriding mountPath from config")
|
||||
commands.getPathCommand.AddPositionalValue(&itemIDOrURL, "item-id-or-url", 1, true, "Docspell item ID or web URL")
|
||||
commands.getURLCommand.AddPositionalValue(&pathOrID, "local-path-or-item-id", 1, true, "local mounted path, item ID, or item URL")
|
||||
|
||||
parser.AttachSubcommand(commands.importCommand, 1)
|
||||
parser.AttachSubcommand(commands.mountCommand, 1)
|
||||
parser.AttachSubcommand(commands.getPathCommand, 1)
|
||||
parser.AttachSubcommand(commands.getURLCommand, 1)
|
||||
return parser, commands
|
||||
}
|
||||
|
||||
func loadConfigAndPassword() (config, string) {
|
||||
@@ -145,9 +150,8 @@ func loadConfigAndPassword() (config, string) {
|
||||
}
|
||||
|
||||
func runImportCommand(cfg config, password string, args []string) {
|
||||
importFlags := flag.NewFlagSet("import", flag.ExitOnError)
|
||||
uploadMissing := importFlags.Bool("upload-missing", os.Getenv("DS_CC_UPLOAD_MISSING") == "true", "true, to upload files to docspell that do not yet exist there")
|
||||
if err := importFlags.Parse(args); err != nil {
|
||||
options, err := parseImportOptions(args)
|
||||
if err != nil {
|
||||
fmt.Println("Error parsing flags:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -161,7 +165,7 @@ func runImportCommand(cfg config, password string, args []string) {
|
||||
}
|
||||
|
||||
fmt.Println("Settings:")
|
||||
if *uploadMissing {
|
||||
if options.uploadMissing {
|
||||
fmt.Println(" - UPLOAD files? YES")
|
||||
fmt.Println(" files not existing in Docspell will be uploaded and will be re-checked in the next run.")
|
||||
} else {
|
||||
@@ -211,7 +215,7 @@ func runImportCommand(cfg config, password string, args []string) {
|
||||
}
|
||||
} else {
|
||||
fmt.Println(" Files does not exist, yet")
|
||||
if *uploadMissing {
|
||||
if options.uploadMissing {
|
||||
fmt.Print(" ...uploading file..")
|
||||
cmd = exec.Command("dsc", "-f", "json", "upload", path)
|
||||
output, err := cmd.Output()
|
||||
@@ -244,6 +248,20 @@ func runImportCommand(cfg config, password string, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
type importOptions struct {
|
||||
uploadMissing bool
|
||||
}
|
||||
|
||||
func parseImportOptions(args []string) (importOptions, error) {
|
||||
options := importOptions{uploadMissing: os.Getenv("DS_CC_UPLOAD_MISSING") == "true"}
|
||||
parser := flaggy.NewParser("import")
|
||||
parser.Description = "Scan configured import folders, archive confirmed files, and optionally upload missing files"
|
||||
parser.DisableShowVersionWithVersion()
|
||||
parser.ShowHelpOnUnexpected = true
|
||||
parser.Bool(&options.uploadMissing, "", "upload-missing", "true, to upload files to docspell that do not yet exist there")
|
||||
return options, parser.ParseArgs(args)
|
||||
}
|
||||
|
||||
func handleExistingFile(cfg config, fileExistsResponse FileExistsResult) error {
|
||||
// File exists in Docspell
|
||||
items := fileExistsResponse.Items
|
||||
|
||||
Reference in New Issue
Block a user