fix(app): name the config key in capture directory errors
- commit
- 18051453d7b81e4cc79b7c29976a9b25394770d5
- author
- brice · · 2026-09-30 16:01 UTC
- parent
- a7c942d
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +13 -13
M README.md +1 -1
| @@ -38,7 +38,7 @@ video.dir = ~/Videos | |||
| 38 | 38 | ``` | |
| 39 | 39 | ||
| 40 | 40 | - `selector.grid`: `on` (default) or `off` to hide the guides on startup. | |
| 41 | - | - `screenshot.dir`, `video.dir`: an absolute path or `~/…`; relative paths are errors. The directory is created (private) when missing. Absent, the XDG defaults below apply. | |
| 41 | + | - `screenshot.dir`, `video.dir`: an absolute path or `~/…`; relative paths are errors. Absent, the XDG defaults below apply. Before the selector opens, nefercap creates (private) and checks each directory the command can use: both for all-in-one, one for `shot` or `rec`. | |
| 42 | 42 | - `screenshot.output`: `file` (default), `file+clipboard` (save, then copy the same PNG) or `clipboard` (copy only; no file, no path printed). | |
| 43 | 43 | ||
| 44 | 44 | Each key may appear once; blank lines and `#` comment lines are allowed; unknown keys, duplicates and invalid values are errors reported before the selector opens. `screenshot.output` and the directories apply to all-in-one, `shot` and `rec`; the scripted `screenshot` and `record` never read them. On `shot`, `-clipboard` alone copies only, `-clipboard -file PATH` saves and copies, and `-file PATH` alone saves only, whatever `screenshot.output` says. **G** changes visibility for the current monitor's overlay without rewriting the config. Selection dimensions are logical pixels; the captured file's dimensions depend on the monitor scaling. | |
| @@ -38,7 +38,7 @@ video.dir = ~/Videos | |||||
| 38 | ``` | 38 | ``` | ||
| 39 | 39 | ||||
| 40 | - `selector.grid`: `on` (default) or `off` to hide the guides on startup. | 40 | - `selector.grid`: `on` (default) or `off` to hide the guides on startup. | ||
| 41 | - | - `screenshot.dir`, `video.dir`: an absolute path or `~/…`; relative paths are errors. The directory is created (private) when missing. Absent, the XDG defaults below apply. | 41 | + | - `screenshot.dir`, `video.dir`: an absolute path or `~/…`; relative paths are errors. Absent, the XDG defaults below apply. Before the selector opens, nefercap creates (private) and checks each directory the command can use: both for all-in-one, one for `shot` or `rec`. |
| 42 | - `screenshot.output`: `file` (default), `file+clipboard` (save, then copy the same PNG) or `clipboard` (copy only; no file, no path printed). | 42 | - `screenshot.output`: `file` (default), `file+clipboard` (save, then copy the same PNG) or `clipboard` (copy only; no file, no path printed). | ||
| 43 | 43 | ||||
| 44 | Each key may appear once; blank lines and `#` comment lines are allowed; unknown keys, duplicates and invalid values are errors reported before the selector opens. `screenshot.output` and the directories apply to all-in-one, `shot` and `rec`; the scripted `screenshot` and `record` never read them. On `shot`, `-clipboard` alone copies only, `-clipboard -file PATH` saves and copies, and `-file PATH` alone saves only, whatever `screenshot.output` says. **G** changes visibility for the current monitor's overlay without rewriting the config. Selection dimensions are logical pixels; the captured file's dimensions depend on the monitor scaling. | 44 | Each key may appear once; blank lines and `#` comment lines are allowed; unknown keys, duplicates and invalid values are errors reported before the selector opens. `screenshot.output` and the directories apply to all-in-one, `shot` and `rec`; the scripted `screenshot` and `record` never read them. On `shot`, `-clipboard` alone copies only, `-clipboard -file PATH` saves and copies, and `-file PATH` alone saves only, whatever `screenshot.output` says. **G** changes visibility for the current monitor's overlay without rewriting the config. Selection dimensions are logical pixels; the captured file's dimensions depend on the monitor scaling. | ||
M internal/app/destination_test.go +10 -10
| @@ -25,20 +25,20 @@ func TestCheckDirsBeforeSelector(t *testing.T) { | |||
| 25 | 25 | name string | |
| 26 | 26 | options cli.Options | |
| 27 | 27 | settings config.Settings | |
| 28 | - | wantErr bool | |
| 28 | + | wantKey string // config key named in the error; empty means no error | |
| 29 | 29 | }{ | |
| 30 | - | {"all-in-one bad video dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: file, ScreenshotDir: good}, true}, | |
| 31 | - | {"all-in-one bad screenshot dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: good, ScreenshotDir: file}, true}, | |
| 32 | - | {"all-in-one clipboard skips screenshot dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: good, ScreenshotDir: file, Output: config.OutputClipboard}, false}, | |
| 33 | - | {"shot ignores video dir", cli.Options{Command: cli.Shot}, config.Settings{VideoDir: file, ScreenshotDir: good}, false}, | |
| 34 | - | {"rec ignores screenshot dir", cli.Options{Command: cli.Rec}, config.Settings{VideoDir: good, ScreenshotDir: file}, false}, | |
| 35 | - | {"rec bad video dir", cli.Options{Command: cli.Rec}, config.Settings{VideoDir: file}, true}, | |
| 36 | - | {"explicit -file skips dirs", cli.Options{Command: cli.Shot, Path: "x.png"}, config.Settings{ScreenshotDir: file}, false}, | |
| 30 | + | {"all-in-one bad video dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: file, ScreenshotDir: good}, "video.dir"}, | |
| 31 | + | {"all-in-one bad screenshot dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: good, ScreenshotDir: file}, "screenshot.dir"}, | |
| 32 | + | {"all-in-one clipboard skips screenshot dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: good, ScreenshotDir: file, Output: config.OutputClipboard}, ""}, | |
| 33 | + | {"shot ignores video dir", cli.Options{Command: cli.Shot}, config.Settings{VideoDir: file, ScreenshotDir: good}, ""}, | |
| 34 | + | {"rec ignores screenshot dir", cli.Options{Command: cli.Rec}, config.Settings{VideoDir: good, ScreenshotDir: file}, ""}, | |
| 35 | + | {"rec bad video dir", cli.Options{Command: cli.Rec}, config.Settings{VideoDir: file}, "video.dir"}, | |
| 36 | + | {"explicit -file skips dirs", cli.Options{Command: cli.Shot, Path: "x.png"}, config.Settings{ScreenshotDir: file}, ""}, | |
| 37 | 37 | } { | |
| 38 | 38 | t.Run(tc.name, func(t *testing.T) { | |
| 39 | 39 | err := checkDirs(tc.options, tc.settings) | |
| 40 | - | if tc.wantErr { | |
| 41 | - | assert.Error(t, err) | |
| 40 | + | if tc.wantKey != "" { | |
| 41 | + | assert.ErrorContains(t, err, tc.wantKey+":") | |
| 42 | 42 | } else { | |
| 43 | 43 | assert.NoError(t, err) | |
| 44 | 44 | } | |
| @@ -25,20 +25,20 @@ func TestCheckDirsBeforeSelector(t *testing.T) { | |||||
| 25 | name string | 25 | name string | ||
| 26 | options cli.Options | 26 | options cli.Options | ||
| 27 | settings config.Settings | 27 | settings config.Settings | ||
| 28 | - | wantErr bool | 28 | + | wantKey string // config key named in the error; empty means no error |
| 29 | }{ | 29 | }{ | ||
| 30 | - | {"all-in-one bad video dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: file, ScreenshotDir: good}, true}, | 30 | + | {"all-in-one bad video dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: file, ScreenshotDir: good}, "video.dir"}, |
| 31 | - | {"all-in-one bad screenshot dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: good, ScreenshotDir: file}, true}, | 31 | + | {"all-in-one bad screenshot dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: good, ScreenshotDir: file}, "screenshot.dir"}, |
| 32 | - | {"all-in-one clipboard skips screenshot dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: good, ScreenshotDir: file, Output: config.OutputClipboard}, false}, | 32 | + | {"all-in-one clipboard skips screenshot dir", cli.Options{Command: cli.AllInOne}, config.Settings{VideoDir: good, ScreenshotDir: file, Output: config.OutputClipboard}, ""}, |
| 33 | - | {"shot ignores video dir", cli.Options{Command: cli.Shot}, config.Settings{VideoDir: file, ScreenshotDir: good}, false}, | 33 | + | {"shot ignores video dir", cli.Options{Command: cli.Shot}, config.Settings{VideoDir: file, ScreenshotDir: good}, ""}, |
| 34 | - | {"rec ignores screenshot dir", cli.Options{Command: cli.Rec}, config.Settings{VideoDir: good, ScreenshotDir: file}, false}, | 34 | + | {"rec ignores screenshot dir", cli.Options{Command: cli.Rec}, config.Settings{VideoDir: good, ScreenshotDir: file}, ""}, |
| 35 | - | {"rec bad video dir", cli.Options{Command: cli.Rec}, config.Settings{VideoDir: file}, true}, | 35 | + | {"rec bad video dir", cli.Options{Command: cli.Rec}, config.Settings{VideoDir: file}, "video.dir"}, |
| 36 | - | {"explicit -file skips dirs", cli.Options{Command: cli.Shot, Path: "x.png"}, config.Settings{ScreenshotDir: file}, false}, | 36 | + | {"explicit -file skips dirs", cli.Options{Command: cli.Shot, Path: "x.png"}, config.Settings{ScreenshotDir: file}, ""}, |
| 37 | } { | 37 | } { | ||
| 38 | t.Run(tc.name, func(t *testing.T) { | 38 | t.Run(tc.name, func(t *testing.T) { | ||
| 39 | err := checkDirs(tc.options, tc.settings) | 39 | err := checkDirs(tc.options, tc.settings) | ||
| 40 | - | if tc.wantErr { | 40 | + | if tc.wantKey != "" { |
| 41 | - | assert.Error(t, err) | 41 | + | assert.ErrorContains(t, err, tc.wantKey+":") |
| 42 | } else { | 42 | } else { | ||
| 43 | assert.NoError(t, err) | 43 | assert.NoError(t, err) | ||
| 44 | } | 44 | } | ||
M internal/app/interactive.go +2 -2
| @@ -64,12 +64,12 @@ func checkDirs(options cli.Options, settings config.Settings) error { | |||
| 64 | 64 | all := options.Command == cli.AllInOne | |
| 65 | 65 | if all || options.Command == cli.Rec { | |
| 66 | 66 | if _, err := destination.VideoIn(settings.VideoDir); err != nil { | |
| 67 | - | return err | |
| 67 | + | return fmt.Errorf("video.dir: %w", err) | |
| 68 | 68 | } | |
| 69 | 69 | } | |
| 70 | 70 | if (all || options.Command == cli.Shot) && !options.Clipboard && settings.Output != config.OutputClipboard { | |
| 71 | 71 | if _, err := destination.ScreenshotIn(settings.ScreenshotDir); err != nil { | |
| 72 | - | return err | |
| 72 | + | return fmt.Errorf("screenshot.dir: %w", err) | |
| 73 | 73 | } | |
| 74 | 74 | } | |
| 75 | 75 | return nil | |
| @@ -64,12 +64,12 @@ func checkDirs(options cli.Options, settings config.Settings) error { | |||||
| 64 | all := options.Command == cli.AllInOne | 64 | all := options.Command == cli.AllInOne | ||
| 65 | if all || options.Command == cli.Rec { | 65 | if all || options.Command == cli.Rec { | ||
| 66 | if _, err := destination.VideoIn(settings.VideoDir); err != nil { | 66 | if _, err := destination.VideoIn(settings.VideoDir); err != nil { | ||
| 67 | - | return err | 67 | + | return fmt.Errorf("video.dir: %w", err) |
| 68 | } | 68 | } | ||
| 69 | } | 69 | } | ||
| 70 | if (all || options.Command == cli.Shot) && !options.Clipboard && settings.Output != config.OutputClipboard { | 70 | if (all || options.Command == cli.Shot) && !options.Clipboard && settings.Output != config.OutputClipboard { | ||
| 71 | if _, err := destination.ScreenshotIn(settings.ScreenshotDir); err != nil { | 71 | if _, err := destination.ScreenshotIn(settings.ScreenshotDir); err != nil { | ||
| 72 | - | return err | 72 | + | return fmt.Errorf("screenshot.dir: %w", err) |
| 73 | } | 73 | } | ||
| 74 | } | 74 | } | ||
| 75 | return nil | 75 | return nil | ||