Use flaggy for CLI argument parsing
This commit is contained in:
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"bazil.org/fuse/fuseutil"
|
||||
"github.com/integrii/flaggy"
|
||||
)
|
||||
|
||||
type docspellFS struct {
|
||||
@@ -54,42 +54,32 @@ type mountedItemMetadata struct {
|
||||
const mountMetadataFileName = ".docspell-cli-metadata.json"
|
||||
|
||||
func runMountCommand(args []string, cfg config, password string) error {
|
||||
fsFlags := flag.NewFlagSet("mount", flag.ExitOnError)
|
||||
query := fsFlags.String("query", cfg.MountQuery, "Docspell item query to expose")
|
||||
limit := fsFlags.Int("limit", 1000, "number of items to fetch per API request")
|
||||
authHeader := fsFlags.String("auth-header", defaultDocspellAuthHeader, "HTTP header used for the Docspell auth token")
|
||||
if err := fsFlags.Parse(args); err != nil {
|
||||
options, err := parseMountOptions(args, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mountPoint := cfg.MountPath
|
||||
if fsFlags.NArg() > 1 {
|
||||
return fmt.Errorf("usage: %s mount [flags] [mountpoint]", filepath.Base(os.Args[0]))
|
||||
}
|
||||
if fsFlags.NArg() == 1 {
|
||||
mountPoint = fsFlags.Arg(0)
|
||||
}
|
||||
if mountPoint == "" {
|
||||
if options.mountPoint == "" {
|
||||
return fmt.Errorf("mount point missing: pass one or set MountPath in config")
|
||||
}
|
||||
if os.Getenv("DOCSPELL_CLI_MOUNT_FOREGROUND") != "1" {
|
||||
return startMountInBackground(args, mountPoint, false)
|
||||
return startMountInBackground(args, options.mountPoint, false)
|
||||
}
|
||||
if err := os.MkdirAll(mountPoint, 0755); err != nil {
|
||||
if err := os.MkdirAll(options.mountPoint, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := NewDocspellClient(cfg.DocspellURL, *authHeader)
|
||||
client := NewDocspellClient(cfg.DocspellURL, options.authHeader)
|
||||
if err := client.Login(context.Background(), cfg.User, password); err != nil {
|
||||
return err
|
||||
}
|
||||
items, err := client.SearchItems(context.Background(), *query, *limit)
|
||||
items, err := client.SearchItems(context.Background(), options.query, options.limit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("search items: %w", err)
|
||||
}
|
||||
root := buildMountTree(context.Background(), client, items)
|
||||
|
||||
conn, err := fuse.Mount(
|
||||
mountPoint,
|
||||
options.mountPoint,
|
||||
fuse.FSName("docspell"),
|
||||
fuse.Subtype("docspell-cli"),
|
||||
fuse.ReadOnly(),
|
||||
@@ -99,10 +89,35 @@ func runMountCommand(args []string, cfg config, password string) error {
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
fmt.Printf("Mounted %d items read-only at %s. Unmount with: fusermount -u %s\n", len(items), mountPoint, mountPoint)
|
||||
fmt.Printf("Mounted %d items read-only at %s. Unmount with: fusermount -u %s\n", len(items), options.mountPoint, options.mountPoint)
|
||||
return fs.Serve(conn, &docspellFS{root: root})
|
||||
}
|
||||
|
||||
type mountOptions struct {
|
||||
query string
|
||||
limit int
|
||||
authHeader string
|
||||
mountPoint string
|
||||
}
|
||||
|
||||
func parseMountOptions(args []string, cfg config) (mountOptions, error) {
|
||||
options := mountOptions{
|
||||
query: cfg.MountQuery,
|
||||
limit: 1000,
|
||||
authHeader: defaultDocspellAuthHeader,
|
||||
mountPoint: cfg.MountPath,
|
||||
}
|
||||
parser := flaggy.NewParser("mount")
|
||||
parser.Description = "Expose Docspell search results as a read-only FUSE filesystem"
|
||||
parser.DisableShowVersionWithVersion()
|
||||
parser.ShowHelpOnUnexpected = true
|
||||
parser.String(&options.query, "", "query", "Docspell item query to expose")
|
||||
parser.Int(&options.limit, "", "limit", "number of items to fetch per API request")
|
||||
parser.String(&options.authHeader, "", "auth-header", "HTTP header used for the Docspell auth token")
|
||||
parser.AddPositionalValue(&options.mountPoint, "mountpoint", 1, false, "mount point, overriding mountPath from config")
|
||||
return options, parser.ParseArgs(args)
|
||||
}
|
||||
|
||||
func startMountInBackground(args []string, mountPoint string, quiet bool) error {
|
||||
mountPoint = cleanMountPath(mountPoint)
|
||||
if isMounted(mountPoint) {
|
||||
@@ -133,18 +148,15 @@ func startMountInBackground(args []string, mountPoint string, quiet bool) error
|
||||
}
|
||||
|
||||
func runGetPathCommand(args []string, cfg config, password string) error {
|
||||
flags := flag.NewFlagSet("get-path", flag.ExitOnError)
|
||||
if err := flags.Parse(args); err != nil {
|
||||
itemIDOrURL, err := parseSingleArgumentCommand("get-path", "item-id-or-url", "Docspell item ID or web URL", args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if flags.NArg() != 1 {
|
||||
return fmt.Errorf("usage: %s get-path <item-id-or-url>", filepath.Base(os.Args[0]))
|
||||
}
|
||||
mountPoint, err := ensureMounted(cfg, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := itemIDFromURLOrID(flags.Arg(0))
|
||||
id := itemIDFromURLOrID(itemIDOrURL)
|
||||
if path, err := friendlyLocalPathForItemID(mountPoint, id); err == nil && path != "" {
|
||||
fmt.Println(path)
|
||||
return nil
|
||||
@@ -183,16 +195,11 @@ func readMountMetadata(mountPoint string) (mountMetadata, error) {
|
||||
}
|
||||
|
||||
func runGetURLCommand(args []string, cfg config, password string) error {
|
||||
flags := flag.NewFlagSet("get-url", flag.ExitOnError)
|
||||
if err := flags.Parse(args); err != nil {
|
||||
id, err := parseSingleArgumentCommand("get-url", "local-path-or-item-id", "local mounted path, item ID, or item URL", args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if flags.NArg() != 1 {
|
||||
return fmt.Errorf("usage: %s get-url <local-path-or-item-id>", filepath.Base(os.Args[0]))
|
||||
}
|
||||
id := flags.Arg(0)
|
||||
if strings.Contains(id, string(os.PathSeparator)) || strings.HasPrefix(id, ".") {
|
||||
var err error
|
||||
id, err = itemIDFromLocalPath(id, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -202,6 +209,15 @@ func runGetURLCommand(args []string, cfg config, password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSingleArgumentCommand(commandName, argName, argDescription string, args []string) (string, error) {
|
||||
var value string
|
||||
parser := flaggy.NewParser(commandName)
|
||||
parser.DisableShowVersionWithVersion()
|
||||
parser.ShowHelpOnUnexpected = true
|
||||
parser.AddPositionalValue(&value, argName, 1, true, argDescription)
|
||||
return value, parser.ParseArgs(args)
|
||||
}
|
||||
|
||||
func ensureMounted(cfg config, password string) (string, error) {
|
||||
mountPoint := cleanMountPath(cfg.MountPath)
|
||||
if mountPoint == "" {
|
||||
|
||||
Reference in New Issue
Block a user