diff --git a/cmd/tg/upload.go b/cmd/tg/upload.go index 0436674..c0f33c6 100644 --- a/cmd/tg/upload.go +++ b/cmd/tg/upload.go @@ -224,6 +224,20 @@ func (a *app) uploadOne( return msgID, nil } +// walkFiles calls fn for each regular file under root, or for root itself when +// it is a file. +func walkFiles(root string, fn func(path string, info fs.FileInfo) error) error { + return filepath.Walk(root, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err // info is nil here + } + if info.IsDir() { + return nil + } + return fn(path, info) + }) +} + func (a *app) newUploadCmd() *cobra.Command { uf := &uploadFlags{docType: newEnumValue("", documentTypes()...)} @@ -247,6 +261,10 @@ The document type is detected from the file's MIME type unless --type is set.`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { arg := args[0] + // Fail before connecting when there is nothing to upload. + if _, err := os.Stat(arg); err != nil { + return errors.Wrap(err, "upload") + } return a.run(cmd.Context(), runParams{auth: authUser}, func(ctx context.Context, api *tg.Client) error { upld := uploader.NewUploader(api). @@ -265,11 +283,7 @@ The document type is detected from the file's MIME type unless --type is set.`, quiet := a.printer.Format() == output.JSON var result uploadResult - if err := filepath.Walk(arg, func(path string, info fs.FileInfo, err error) error { - // Stop if got error, skip if current file is directory. - if info.IsDir() || err != nil { - return err - } + if err := walkFiles(arg, func(path string, info fs.FileInfo) error { id, err := a.uploadOne(ctx, uf, upld, builder, path, info, quiet) if err != nil { return err diff --git a/cmd/tg/upload_test.go b/cmd/tg/upload_test.go new file mode 100644 index 0000000..94da831 --- /dev/null +++ b/cmd/tg/upload_test.go @@ -0,0 +1,37 @@ +package main + +import ( + "io/fs" + "os" + "path/filepath" + "strings" + "testing" +) + +func TestWalkFiles(t *testing.T) { + dir := t.TempDir() + for _, name := range []string{"a.txt", "sub/b.txt"} { + path := filepath.Join(dir, name) + if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(path, []byte("x"), 0o600); err != nil { + t.Fatal(err) + } + } + var got []string + if err := walkFiles(dir, func(path string, _ fs.FileInfo) error { + got = append(got, filepath.Base(path)) + return nil + }); err != nil { + t.Fatal(err) + } + if strings.Join(got, ",") != "a.txt,b.txt" { + t.Errorf("visited %v", got) + } + + // Regression: a missing path used to panic on a nil FileInfo. + if err := walkFiles(filepath.Join(dir, "missing"), func(string, fs.FileInfo) error { return nil }); err == nil { + t.Error("expected an error for a missing path") + } +}