From 2f977d630fad6918fe0039d22ec13d90b89a94a0 Mon Sep 17 00:00:00 2001 From: Colin Okay Date: Tue, 22 Feb 2022 13:46:35 -0600 Subject: prompt with validation and retry --- src/lib.lisp | 55 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/lib.lisp b/src/lib.lisp index 0ea4cda..858675f 100644 --- a/src/lib.lisp +++ b/src/lib.lisp @@ -91,9 +91,24 @@ the directories that appear in the value of that variable." (defun tags-from-oneliner (oneliner) (remove-if-not #'executable-on-system-p (ppcre:split " +" oneliner))) -(defun prompt (prompt &key (out-stream *standard-output*) (in-stream *standard-input*)) - (princ prompt out-stream) (force-output out-stream) - (read-line in-stream)) +(defun prompt (prompt + &key + (out-stream *standard-output*) + (in-stream *standard-input*) + (expect (constantly t)) + retry-text) + + (loop + with to-prompt = prompt + with should-retry-p = t + while should-retry-p + for line = (progn + (princ to-prompt out-stream) (force-output out-stream) + (setf to-prompt (or retry-text prompt)) + (read-line in-stream)) + when (funcall expect line) + do (setf should-retry-p nil) + finally (return line))) (defun cached-result (&optional n) (when (uiop:file-exists-p (last-search-file)) @@ -158,8 +173,10 @@ the directories that appear in the value of that variable." (format t "Copied oneliner to clipboard~%")) (progn (ensure-config) - (format t "Attempting to run: ~%") - (princ ol) (terpri) + (format t "Attempting to run:~%") + (princ ol) + (princ #\newline) + (princ #\newline) (run-with-shell ol :shell-name (or (get-shell) "bash"))))) @@ -188,27 +205,41 @@ the directories that appear in the value of that variable." (handle-run-oneliner oneliner (or force-clip (equalp runstyle "manual"))))))) +(defun valid-oneliner-string-p (string) + (and (not (find #\newline string)) + (tags-from-oneliner string))) + +(defun valid-brief-description-p (string) + (<= (length string) 72)) + +(defun valid-runstyle-p (string) + (member string '("auto" "manual") :test 'equalp)) (defun add-new-oneliner () (ensure-config) (assert (api-token) () "Cannot add a oneliner without an api token.") (let* ((oneliner - (prompt "Oneliner: ")) + (prompt "Oneliner: " + :expect 'valid-oneliner-string-p + :retry-text "Oneliners must contain at least one command: ")) (init-tags (tags-from-oneliner oneliner)) (brief - (prompt "Brief Description: ")) + (prompt "Brief Description: " + :expect 'valid-brief-description-p + :retry-text "Too long. Must be <= 72 characters: ")) (tags (append init-tags (ppcre:split " +" (prompt (format nil "Tags in addition to ~{~a ~} ?" init-tags))))) + (runstyle + (string-upcase + (prompt "Runstyle (auto or manual): " + :expect 'valid-runstyle-p + :retry-text "Must be (auto or manual): "))) (explanation (when (y-or-n-p "Provide an explanation?") - (string-from-editor))) - (runstyle - (if (y-or-n-p "Does this oneliner invoke a GUI, a TUI, or will it require user input?") - "MANUAL" - "AUTO"))) + (string-from-editor)))) (api:request-with (:host (host) :body (jonathan:to-json -- cgit v1.2.3