aboutsummaryrefslogtreecommitdiff
path: root/lib/client.lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/client.lisp')
-rw-r--r--lib/client.lisp255
1 files changed, 174 insertions, 81 deletions
diff --git a/lib/client.lisp b/lib/client.lisp
index fe29932..61c3130 100644
--- a/lib/client.lisp
+++ b/lib/client.lisp
@@ -16,12 +16,16 @@
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
(in-package :oneliners.cli)
-;; NOTE WHILE HACKING. Each of the functions below that make HTTP
-;; requests is meant to be called within the body of a
-;; WITH-LOCAL-STATE form. If you are hacking in the REPL, make sure
-;; to wrap function calls appropriately.
+;;;; >>>>>NOTE WHILE HACKING<<<<<.
+;;;; ----------------------------------------------------------------
+;;;; Each of the functions below that make HTTP requests are meant to
+;;;; be called within the body of a WITH-LOCAL-STATE form. If you are
+;;;; hacking in the REPL, make sure to wrap function calls
+;;;; appropriately.
+;;;; ----------------------------------------------------------------
-;;; GETTING ONELINERS & Displaying oneliners
+
+;;; GETTING ONELINERS & DISPLAYING ONELINERS
(defun search-for-oneliners (terms limit &optional not-flagged-p all-flagged-p newestp)
(assert (loop for term in terms never (find #\, term)) () "Search terms may not contain commas.")
@@ -45,12 +49,18 @@ not in the local cache, try to fetch from configured server."
(defmacro when-oneliner ((var name-or-id) &body body)
"Finds the oneliner with name-or-id and binds it to var before
running the body. If such a oneliner can be found."
- (assert (symbolp var))
(let ((nvar (gensym)))
`(let ((,nvar ,name-or-id))
(a:when-let (,var (the-oneliner ,nvar))
,@body))))
+(defmacro when-draft ((var name) &body body)
+ "Like when-oneliner but restricts itself to local drafts."
+ (let ((nvar (gensym)))
+ `(let ((,nvar ,name))
+ (a:when-let (,var (fetch-draft ,nvar))
+ ,@body))))
+
(defun newest-oneliners (&optional limit)
(let ((response
(if limit
@@ -72,52 +82,62 @@ running the body. If such a oneliner can be found."
(princ #\newline)
(princ (oneliner-explanation ol)))))
+(defun print-drafts ()
+ (when *drafts*
+ (format t (concatenate 'string "~%~" (prin1-to-string *term-width*) "< ~;DRAFTS~; ~>~%"))
+ (dolist (draft *drafts*)
+ (print-oneliner-result-for-user (cdr draft)))))
+
;;; RUNNING ONELINERS
(defvar *ol-output-timeout* 1)
-(defun run-item (ident args &key force-clip (timeout nil timeout-p))
- (when-oneliner (ol ident)
- (let ((*ol-output-timeout* (if timeout-p timeout *ol-output-timeout*)))
- (bind-vars-and-run-oneliner ol args force-clip))))
+(defun run-item (ident args &key force-clip (timeout nil timeout-p) draftp verbose confirm)
+ "Runs a oneliner identified by IDENT (if available) with arguments ARGS."
+ (let ((ol (if draftp (fetch-draft ident) (the-oneliner ident))))
+ (when ol
+ (let ((*ol-output-timeout* (if timeout-p timeout *ol-output-timeout*)))
+ (bind-vars-and-run-oneliner ol args force-clip verbose confirm)))))
-(defun bind-vars-and-run-oneliner (ol args &optional force-clip)
+(defun bind-vars-and-run-oneliner (ol args &optional force-clip verbose confirm)
(let* ((oneliner (oneliner-oneliner ol))
(runstyle (oneliner-runstyle ol))
(pos-args (get-positional-arguments ol))
(named-args (get-named-arguments ol)))
+
+ (loop for param in pos-args
+ for arg in args
+ do (setf oneliner (str:replace-all param arg oneliner)))
+ ;; substitute named args
+
+ (setf args
+ (mapcar (lambda (s) (str:split "=" s))
+ (nthcdr (length pos-args) args)))
+ (loop for var in named-args
+ for bound = (assoc (subseq var 1) args :test #'equal)
+ when bound
+ do (setf oneliner
+ (str:replace-all var (second bound) oneliner)))
+
(when (or (not (oneliner-isflagged ol))
(y-or-n-p "This oneliner is flagged. Are you sure you want to run it?"))
+ (when (or verbose confirm)
+ (format t "Attempting to run:~%")
+ (princ oneliner)
+ (princ #\newline))
;; substitute positional args
- (loop for param in pos-args
- for arg in args
- do (setf oneliner (str:replace-all param arg oneliner)))
- ;; substitute named args
- (setf args
- (mapcar (lambda (s) (str:split "=" s))
- (nthcdr (length pos-args) args)))
- (loop for var in named-args
- for bound = (assoc (subseq var 1) args :test #'equal)
- when bound
- do (setf oneliner
- (str:replace-all var (second bound) oneliner)))
-
- (handle-run-oneliner oneliner (or force-clip (equalp runstyle "manual"))))))
+ (when (or (not confirm)
+ (y-or-n-p "Proceed?"))
+ (handle-run-oneliner oneliner (or force-clip (equalp runstyle "manual")))))))
(defun handle-run-oneliner (ol &optional clip)
(if clip
(progn (trivial-clipboard:text ol)
(format t "Copied oneliner to clipboard~%"))
- (progn
- (format t "Attempting to run:~%")
- (princ ol)
- (princ #\newline)
- (princ #\newline)
- (run-with-shell ol :shell-name (or (shell) "bash") :await-output-p *ol-output-timeout*))))
+ (run-with-shell ol :shell-name (or (shell) "bash") :await-output-p *ol-output-timeout*)))
;;; ADDING ONELINERS
-
(defun add-new-oneliner ()
(api-token) ;; fails with error if not set.
;; read each field required to make a onelienr in from a prompt.
@@ -131,8 +151,18 @@ running the body. If such a oneliner can be found."
(prompt "Name (leave blank for none): "
:expect 'valid-oneliner-name-p
:retry-text "Must begin with a letter contain only letters, numbers, - and _.")))
+
+ (draft-name
+ (unless (y-or-n-p "Upload immediately instead of keeping a draft?")
+ (if (plusp (length name))
+ name
+ (prompt "No name was provided, name this draft: "
+ :expect 'valid-oneliner-name-p
+ :retry-text "Must begin with a letter contain only letters, numbers, - and _."))))
+
(init-tags
(parse-oneliner-tags oneliner-string))
+
(brief
(prompt "Brief Description: "
:expect 'valid-brief-description-p
@@ -153,25 +183,34 @@ running the body. If such a oneliner can be found."
(when (y-or-n-p "Provide an explanation?")
(string-from-editor
(format nil "~a~%~%" oneliner-string)))))
- (api:request-with
- (:body (jonathan:to-json
- (list :oneliner oneliner-string
- :name (if (plusp (length name)) name :null)
- :tags tags
- :brief brief
- :explanation explanation
- :runstyle runstyle))
- :content-type "application/json")
- (api:post--oneliner :token (api-token)) ;TODO: update api to return the instance created.
- (format t "Added~%"))))
+ (let ((local
+ (make-oneliner
+ :oneliner oneliner-string
+ :name name
+ :tags tags
+ :brief brief
+ :explanation explanation
+ :runstyle runstyle)))
+ (if draft-name
+ ;; if this is a draft, save it to disk.
+ (progn
+ (put-draft draft-name local)
+ (format t "Saved draft ~a~%Do `ol --draft run ~a` to test~%"
+ draft-name
+ draft-name))
+ ;; otherwise, format the oneliner as json and make a request
+ ;; to create a new oneliner in the wiki
+ (api:request-with
+ (:body (oneliner-to-json-body local)
+ :content-type "application/json")
+ (api:post--oneliner :token (api-token)) ;TODO: update api to return the instance created.
+ (format t "Added Oneliner~%"))))))
;;; EDITING ONELINERS
-(defun edit-item (ident)
+(defun edit-item (ident &optional draftp)
(api-token) ;; fails with error if not set.
- (when-oneliner (ol ident)
- ;; Like adding a oneliner, read each field in from a prompt.
- ;; Here, prefil the field with its current value.
+ (let ((ol (if draftp (fetch-draft ident) (the-oneliner ident))))
(let* ((oneliner-string
(prompt "Oneliner: "
:expect 'valid-oneliner-string-p
@@ -184,13 +223,23 @@ running the body. If such a oneliner can be found."
:expect 'valid-oneliner-name-p
:retry-text "Must begin with a letter contain only letters, numbers, - and _."
:prefill (or (oneliner-name ol) ""))))
+
+ (draft-name
+ (unless (y-or-n-p "Upload edits immediately instead of keeping a draft?")
+ (if (plusp (length name))
+ name
+ (prompt "No name was provided, name this draft: "
+ :expect 'valid-oneliner-name-p
+ :retry-text "Must begin with a letter contain only letters, numbers, - and _."))))
(brief
(prompt "Brief Description: "
:expect 'valid-brief-description-p
:retry-text "Too long. Must be <= 72 characters: "
:prefill (oneliner-brief ol)))
+
(init-tags
(parse-oneliner-tags oneliner-string))
+
(tags
(progn
(format t "Tags include: ~{~a ~}~%" init-tags)
@@ -208,49 +257,93 @@ running the body. If such a oneliner can be found."
:expect 'valid-runstyle-p
:retry-text "Must be (auto or manual): "
:prefill (oneliner-runstyle ol))))
+
(explanation
- (when (y-or-n-p "Provide an explanation?")
+ (when (y-or-n-p "Alter the explanation?")
(string-from-editor (oneliner-explanation ol)))))
- (let ((new-item
- (list :oneliner oneliner-string
- :tags tags
- :brief brief
- :name (if (plusp (length name)) name :null)
- :explanation explanation
- :runstyle runstyle)))
- (api:request-with
- (:body (jonathan:to-json new-item)
- :content-type "application/json")
+
+ (let ((local
+ (make-oneliner
+ :id (oneliner-id ol)
+ :oneliner oneliner-string
+ :name (if (plusp (length name)) name :null)
+ :tags tags
+ :brief brief
+ :explanation explanation
+ :runstyle runstyle)))
+ (if draft-name
+ (progn
+ (put-draft draft-name local)
+ (format t "Saved draft ~a~%Do `ol --draft run ~a` to test~%"
+ draft-name
+ draft-name))
+ (api:request-with
+ (:body (oneliner-to-json-body local)
+ :content-type "application/json")
+ (api:patch--oneliner-entry-edit (oneliner-id ol) :token (api-token))
+ ;(merge-oneliners (list new-item)) ;;TODO: this is broken, wait for API update.
+ (format t "Edits accepted~%")))))))
+
+;;TODO: need to sync cache here.
+(defun publish-draft (name)
+ (when-draft (ol name)
+ (api:request-with
+ (:body (oneliner-to-json-body ol)
+ :content-type "application/json")
+ (if (oneliner-id ol)
(api:patch--oneliner-entry-edit (oneliner-id ol) :token (api-token))
- (merge-oneliners (list new-item)) ;;TODO: this is broken, wait for API update.
- (format t "OK~%"))))))
+ (api:post--oneliner :token (api-token)))
+ ;; if that worked, no http error occured, so this next part will run
+ (drop-draft name)
+ (format t "Draft ~a published and removed from drafts.~%" name))))
;;; ADMIN OF ONELINER ENTRIES
+(defun delete-item (ident)
+ (when-oneliner (ol ident)
+ (api:delete--oneliner-oneliner
+ ident
+ :token (api-token))
+ ;; if we've made it this far no http error has been returned,
+ ;; hence we can delete it from the cache
+ (remove-from-cache ident)))
+
(defun flag-item (ident)
+ (when-oneliner (ol ident)
+ (api:put--oneliner-entry-flag
+ ident
+ :token (api-token)
+ :value "true")
+ ;; no http error, so we flag the cached version, ol.
+ (setf (oneliner-isflagged ol) t)))
+
+(defun unflag-item (ident)
+ (when-oneliner (ol ident)
+ (api:put--oneliner-entry-flag
+ ident
+ :token (api-token)
+ :value "false")
+ ;; no http error, so we can unflag the cached version, ol
+ (setf (oneliner-isflagged ol) nil)))
+
+(defun lock-item (ident)
+ (when-oneliner (ol ident)
+ (api:put--oneliner-oneliner-locked
+ ident
+ :token (api-token)
+ :value "true")
+ ;; no http error, so we can lock the cached version, ol
+ (setf (oneliner-islocked ol) t)))
+
+(defun unlock-item (ident)
(when-oneliner (ol ident)
- (api:put--oneliner-entry-flag (oneliner-id ol)
- :token (api-token)
- :value "true")))
-
-(defun unflag-item (item-number)
- (when-oneliner (ol item-number)
- (api:put--oneliner-entry-flag (oneliner-id ol)
- :token (api-token)
- :value "false")))
-
-(defun lock-item (item-number)
- (when-oneliner (ol item-number)
- (api:put--oneliner-oneliner-locked (oneliner-id ol)
- :token (api-token)
- :value "true")))
-
-(defun unlock-item (item-number)
- (when-oneliner (ol item-number)
- (api:put--oneliner-oneliner-locked (oneliner-id ol)
- :token (api-token)
- :value "false")))
+ (api:put--oneliner-oneliner-locked
+ ident
+ :token (api-token)
+ :value "false")
+ ;; no http error, so we can unlock the cached version, ol
+ (setf (oneliner-islocked ol) nil)))
;;; ACCOUNT AND INVITE STUFF