diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-24 04:25:24 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-24 04:25:24 -0500 |
| commit | 49038c418ead0adc83ffc8fce43c0cb6da9813df (patch) | |
| tree | 50964465888b11bf5e2626f29478149e9f1fa64f | |
| parent | 175aad0b1a3dfec37f189ba0dcf34d3ba61b50eb (diff) | |
| download | dotemacs-49038c418ead0adc83ffc8fce43c0cb6da9813df.tar.gz dotemacs-49038c418ead0adc83ffc8fce43c0cb6da9813df.zip | |
refactor(org-drill): share one validated drill-file selector
org-capture-config.el and org-drill-config.el each scanned drill-dir with an inline directory-files call, so a missing, empty, or unreadable drill-dir surfaced as a low-level directory-files error or an empty completing-read, depending on which command ran. Added cj/--drill-files-or-error, the single validated entry point: it signals a clear user-error when the directory is missing, unreadable, or has no drill files, and otherwise returns the list. cj/--drill-pick-file and both drill capture templates now route through it. The pure cj/--drill-files-in primitive and its tests are unchanged. Tests cover missing dir, empty dir, a non-org-only dir, and a normal listing.
| -rw-r--r-- | modules/org-capture-config.el | 13 | ||||
| -rw-r--r-- | modules/org-drill-config.el | 15 | ||||
| -rw-r--r-- | tests/test-org-drill-config--drill-files-or-error.el | 56 |
3 files changed, 73 insertions, 11 deletions
diff --git a/modules/org-capture-config.el b/modules/org-capture-config.el index 5cd76f0a..39cafe03 100644 --- a/modules/org-capture-config.el +++ b/modules/org-capture-config.el @@ -18,6 +18,7 @@ (defvar org-capture-templates) (defvar org-complex-heading-regexp-format) +(declare-function cj/--drill-pick-file "org-drill-config") (declare-function org-at-encrypted-entry-p "org-crypt") (declare-function org-at-heading-p "org") (declare-function org-back-to-heading "org") @@ -230,21 +231,13 @@ Captured On: %U" :prepend t) ("d" "Drill Question" entry - (file (lambda () - (let ((files (directory-files drill-dir nil "^[^.].*\\.org$"))) - (expand-file-name - (completing-read "Choose file: " files) - drill-dir)))) + (file (lambda () (cj/--drill-pick-file drill-dir))) "* Item :drill:\n%? ** Answer\n%i\nSource: [[%:link][%:description]] Captured On: %U" :prepend t) ("f" "Drill Question (from PDF)" entry - (file (lambda () - (let ((files (directory-files drill-dir nil "^[^.].*\\.org$"))) - (expand-file-name - (completing-read "Choose file: " files) - drill-dir)))) + (file (lambda () (cj/--drill-pick-file drill-dir))) "* Item :drill:\n%? ** Answer\n%(cj/org-capture-pdf-active-region) Source: [[%L][%(buffer-name (org-capture-get :original-buffer))]] diff --git a/modules/org-drill-config.el b/modules/org-drill-config.el index 79fdb027..c3852ed1 100644 --- a/modules/org-drill-config.el +++ b/modules/org-drill-config.el @@ -32,10 +32,23 @@ "Return the drill Org file names directly inside DIR (no leading dots)." (directory-files dir nil "^[^.].*\\.org$")) +(defun cj/--drill-files-or-error (dir) + "Return the drill Org files in DIR, or signal a clear `user-error'. +Errors when DIR is missing, unreadable, or has no drill files, so callers +fail with an actionable message instead of a low-level `directory-files' +error or an empty `completing-read'. This is the single entry point the +drill commands and the drill capture templates share." + (unless (and (file-directory-p dir) (file-readable-p dir)) + (user-error "Drill directory missing or unreadable: %s" dir)) + (let ((files (cj/--drill-files-in dir))) + (unless files + (user-error "No drill files (.org) found in %s" dir)) + files)) + (defun cj/--drill-pick-file (dir) "Prompt for one of the drill Org files in DIR; return its absolute path." (expand-file-name - (completing-read "Choose flashcard file: " (cj/--drill-files-in dir) nil t) + (completing-read "Choose flashcard file: " (cj/--drill-files-or-error dir) nil t) dir)) (defun cj/--drill-pick-dir (other-dir) diff --git a/tests/test-org-drill-config--drill-files-or-error.el b/tests/test-org-drill-config--drill-files-or-error.el new file mode 100644 index 00000000..3b980358 --- /dev/null +++ b/tests/test-org-drill-config--drill-files-or-error.el @@ -0,0 +1,56 @@ +;;; test-org-drill-config--drill-files-or-error.el --- Tests for validated drill file listing -*- lexical-binding: t; -*- + +;;; Commentary: +;; Unit tests for cj/--drill-files-or-error, the single validated entry point +;; that drill capture templates and the drill commands share. It returns the +;; drill files in a directory or signals a clear `user-error' when the +;; directory is missing, unreadable, or empty — instead of leaking a low-level +;; error from `directory-files' or handing `completing-read' an empty list. + +;;; Code: + +(require 'ert) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(require 'org-drill-config) + +;;; Normal Cases + +(ert-deftest test-org-drill--files-or-error-normal-lists-org-files () + "Normal: a directory with drill files returns their names." + (let ((dir (make-temp-file "cj-drill-or-error-" t))) + (unwind-protect + (progn + (write-region "" nil (expand-file-name "a.org" dir)) + (write-region "" nil (expand-file-name "b.org" dir)) + (should (equal '("a.org" "b.org") + (cj/--drill-files-or-error dir)))) + (delete-directory dir t)))) + +;;; Boundary Cases + +(ert-deftest test-org-drill--files-or-error-boundary-empty-dir-signals () + "Boundary: an existing but empty directory signals a clear user-error." + (let ((dir (make-temp-file "cj-drill-or-error-empty-" t))) + (unwind-protect + (should-error (cj/--drill-files-or-error dir) :type 'user-error) + (delete-directory dir t)))) + +(ert-deftest test-org-drill--files-or-error-boundary-only-non-org-signals () + "Boundary: a directory with no .org files signals a user-error." + (let ((dir (make-temp-file "cj-drill-or-error-nonorg-" t))) + (unwind-protect + (progn + (write-region "" nil (expand-file-name "notes.txt" dir)) + (should-error (cj/--drill-files-or-error dir) :type 'user-error)) + (delete-directory dir t)))) + +;;; Error Cases + +(ert-deftest test-org-drill--files-or-error-missing-dir-signals-user-error () + "Error: a missing directory signals a `user-error', not a low-level error." + (should-error (cj/--drill-files-or-error "/no/such/cj-drill/dir/") + :type 'user-error)) + +(provide 'test-org-drill-config--drill-files-or-error) +;;; test-org-drill-config--drill-files-or-error.el ends here |
