summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Okay <okay@toyful.space>2022-03-26 11:39:34 -0500
committerColin Okay <okay@toyful.space>2022-03-26 11:39:34 -0500
commitd81bcd3d57b51f95a27f40bb2ba9264d5d805b69 (patch)
tree1150bcef4517eeac26b7077736eb3b61afa05c08
parent95ba9f0b4b4ca9e53187791db8a3e58648fb038a (diff)
[redesign] to be more consistent and less awkward
Though sometimes more verbose. This redesign has dropped the request-with macro and simply generates a package exporting only function names. those functions make requests to the app.
-rw-r--r--lazybones-client.lisp230
1 files changed, 106 insertions, 124 deletions
diff --git a/lazybones-client.lisp b/lazybones-client.lisp
index d5dac4f..87f5e90 100644
--- a/lazybones-client.lisp
+++ b/lazybones-client.lisp
@@ -18,134 +18,116 @@
(defpackage #:lazybones-client
(:use #:cl)
+ (:local-nicknames (#:a #:alexandria-2))
(:export #:generate-client-system))
-;; dummy dexador package to keep lisp from complainging during code
-;; generation
+(in-package :lazybones-client)
-(defpackage #:dexador
- (:export #:get #:put #:post #:head #:patch #:delete))
-(defpackage #:lazybones-client.shared
- (:intern #:req-string)
- (:export #:*host* #:*body* #:*cookies* #:*headers*))
-(in-package :lazybones-client)
+(defun endpoint-defun-name (ep)
+ "Returns the string name of a defun for making requests to
+endpoint EP."
+ (with-output-to-string (*standard-output*)
+ (princ (string-downcase (symbol-name (lazybones::endpoint-method ep))))
+ (princ "/")
+ (loop for (term . more) on (lazybones::endpoint-dispatch-pattern ep)
+ when (and (stringp term) (plusp (length term)))
+ do (princ term)
+ when (listp term)
+ do (princ (car term))
+ when more
+ do (princ "-"))))
+
+(defun endpoint-defun-route-var-names (ep)
+ "Returns a list of strings representing the names of route variables
+extracted from endpoint EP, to be used as variable names in the defun
+for making requests to that endpoint."
+ (mapcar 'symbol-name
+ (lazybones::endpoint-route-vars ep)))
+
+(defun endpoint-defun-query-var-names (ep)
+ "Returns a list of strings representing the names of query parameter
+variables extraced from the endpoint EP, to be used as variable names
+in the defun for making request to that endpoint."
+ (mapcar (a:compose #'symbol-name #'first)
+ (lazybones::endpoint-params ep)))
+
+(defun endpoint-defun-lambda-list (ep)
+ "Returns a string representation of the lambda list of the defun
+for making requests to endpoint EP."
+ (format
+ nil
+ "(%host% %headers% %cookies% ~:[~;%content-type% %body% ~]&key ~{~a ~})"
+ (member (lazybones::endpoint-method ep) '(:post :put :patch))
+ (append
+ (endpoint-defun-route-var-names ep)
+ (endpoint-defun-query-var-names ep))))
+
+
+(defun endpoint-defun-dexador-uri-route-format-string (ep)
+ "Returns a string representing a format string, intended to be
+ embedded into the body of a defun for making requests to the
+ endpoint Ep. It is designed to be passed to FORMAT, where route
+ variables are substituted into the string."
+ (str:join "/"
+ (mapcar (lambda (x) (if (listp x) "~a" x))
+ (lazybones::endpoint-dispatch-pattern ep))))
+
+(defun endpoint-defun-dexador-uri-route-query-format-string (ep)
+ "Returns a string representing a format string, intended to be
+ embedded into the body of a defun for making requests to the
+ endpoint EP. It is desienged to be passed to FORMAT, where query
+ paramters are substituted into the string, if they exist."
+ (str:join "&"
+ (loop for varname in (endpoint-defun-query-var-names ep)
+ collect (with-output-to-string (*standard-output*)
+ (princ "~:[~;")
+ (princ (string-upcase varname))
+ (princ "=~a~]")))))
+
+(defun endpoint-defun-dexador-request-uri (app ep)
+ "Returns a string representation of code that generates a URI for
+ passing to the dexador request function within the body of the defun
+ for making requests to the endpoint EP of the application APP."
+ (concatenate
+ 'string
+ "(format nil "
+ "\""
+ "~a"
+ (lazybones::app-prefix app)
+ (endpoint-defun-dexador-uri-route-format-string ep)
+ "?"
+ (endpoint-defun-dexador-uri-route-query-format-string ep)
+ "\" "
+ (str:join " " (endpoint-defun-route-var-names ep))
+ (str:join " " (endpoint-defun-query-var-names ep))))
+
+(defun endpoint-defun-body (app ep)
+ "Returns a string representation of the function body of a defun
+ for making requests to the endpoint EP in the app APP."
+ (format
+ nil
+ "(dexador:~a~%~s~%~{~a~%~})"
+ (string-downcase (symbol-name (lazybones::endpoint-method ep)))
+ (endpoint-defun-dexador-request-uri app ep)
+ '(":content %body%"
+ ":cookie-jar %cookies%"
+ ":headers (append (when %content-type% (list (cons \"Content-Type\" %content-type%))) %headers%)")))
+
+(defun generate-defun-for-endpoint (app ep)
+ "Returns a string representation of a defun form for a function that
+makes a request to the endpoint EP."
+ (format nil
+ "(defun ~a ~a~%~s~%~a)"
+ (endpoint-defun-name ep)
+ (endpoint-defun-lambda-list ep)
+ (lazybones::endpoint-documentation ep)
+ (endpoint-defun-body app ep)))
-(defparameter +preamble+
- "(defvar *host* nil
- \"The host to which the client will send its requests.\")
-
- (defvar *body* nil
- \"Body passed to client post, put, and patch requests\")
-
- (defvar *cookies* nil
- \"An instance of CL-COOKIE:COOKIE-JAR.\")
-
- (defvar *headers* nil
- \"A liist of (header-name . header-value) pairs.\")
-
- (defmacro request-with ((&key host body headers content-type cookies) &body forms)
- \"Make a request in a specific context.
-
-HOST is a string, the hostname where the request will be sent. Defaults
-to *HOST*.
-
-BODY should be a string, an alist, or a pathname. Default to *BODY*
-
-HEADERS should be an ALIST of (header-name . header-value) string
-pairs. Defaults to *HEADERS*.
-
-CONTENT-TYPE is a convenience for supplying just the Content-Type
-header.
-
-COOKIES should be an instance of CL-COOKIE:COOKIE-JAR. Defaults to
-*COOKIES*.
-\"
- (let ((content-type-var (gensym))
- (http-error-var (gensym)))
- `(let ((*host* (or ,host *host*))
- (*body* (or ,body *body*))
- (*headers* (or ,headers *headers*))
- (*cookies* (or ,cookies *cookies*))
- (,content-type-var ,content-type))
- (when ,content-type-var
- (push (cons \"Content-Type\" ,content-type-var) *headers*))
- (handler-case (progn ,@forms)
- (dex:http-request-failed (,http-error-var)
- (format *error-output* \"~a -- ~a\"
- (dex:response-status ,http-error-var)
- (dex:response-body ,http-error-var)))))))")
-
-(defun make-defun-name (method dispatch-pattern)
- "Utility for making function names for endpoint request functions."
- (intern
- (with-output-to-string (*standard-output*)
- (princ method) (princ "-")
- (loop for (term . more) on dispatch-pattern
- when (and (stringp term) (plusp (length term)))
- do (princ (string-upcase term))
- when (listp term)
- do (princ (string-upcase (car term)))
- when more
- do (princ "-")))))
-
-(defun endpoint-to-defun-name (ep)
- (with-slots (lazybones::method lazybones::dispatch-pattern) ep
- (make-defun-name lazybones::method lazybones::dispatch-pattern)))
-
-(defun dispatch-pattern-to-format-string (pattern)
- (str:join "/" (mapcar (lambda (x) (if (listp x) "~a" x)) pattern)))
-
-(defun param-to-string (p &optional prefix)
- (if prefix
- `(if ,p (concatenate 'string "&" (symbol-name ',p) "=" (format nil "~a" ,p)) "")
- `(if ,p (concatenate 'string (symbol-name ',p) "=" (format nil "~a" ,p)) "")))
-
-(defun params-to-query-string (params)
- (loop for count from 0
- for p in params
- collect (param-to-string p (plusp count))))
-
-(defun generate-client-function-for-endpoint (app-prefix ep)
- (let ((defun-name
- (endpoint-to-defun-name ep))
- (vars
- (mapcar 'intern (lazybones::endpoint-route-vars ep)))
- (format-string
- (dispatch-pattern-to-format-string (lazybones::endpoint-dispatch-pattern ep)))
- (qparams
- (mapcar
- (lambda (x) (intern (symbol-name (first x))))
- (lazybones::endpoint-params ep)))
- (content-p
- (member (lazybones::endpoint-method ep)
- '(:put :post :patch)))
- (dex-fn
- (intern (symbol-name (lazybones::endpoint-method ep)) :dexador)))
- `(defun ,defun-name (,@vars ,@(when qparams (cons '&key qparams)))
- ,(lazybones::endpoint-documentation ep)
- (let ((lazybones-client.shared::req-string
- (apply #'concatenate
- 'string
- lazybones-client.shared:*host*
- ,app-prefix
- (format nil ,format-string ,@vars)
- ,(when qparams `(when (or ,@qparams)
- (list "?" ,@(params-to-query-string qparams)))))))
- ,(if content-p
- `(if lazybones-client.shared:*body*
- (,dex-fn lazybones-client.shared::req-string :content lazybones-client.shared:*body* :cookie-jar lazybones-client.shared:*cookies* :headers lazybones-client.shared:*headers*)
- (,dex-fn lazybones-client.shared::req-string :cookie-jar lazybones-client.shared:*cookies* :headers lazybones-client.shared:*headers*))
- `(,dex-fn lazybones-client.shared::req-string :cookie-jar lazybones-client.shared:*cookies* :headers lazybones-client.shared:*headers*))))))
-
-
-(defun generate-client-functions-for-app (app)
- (loop for ep in (lazybones::app-endpoints app)
- collect (generate-client-function-for-endpoint (lazybones::app-prefix app) ep)))
(defun all-function-names (app)
- (mapcar 'endpoint-to-defun-name (lazybones::app-endpoints app)))
+ (mapcar 'endpoint-defun-name (lazybones::app-endpoints app)))
(defun app-client-package-name (app)
(format nil "~a-CLIENT" (lazybones::app-name app)))
@@ -163,17 +145,15 @@ COOKIES should be an instance of CL-COOKIE:COOKIE-JAR. Defaults to
(princ #\")
(princ ")))")))
+
(defun generate-defpackage-for-client-of-app (app)
(with-output-to-string (out)
(format
out
"
-(defpackage #:lazybones-client.shared
- (:intern #:req-string)
- (:export #:*host* #:*body* #:*headers* #:*cookies*))
(defpackage #:~a
(:use :cl :lazybones-client.shared)
- (:export #:*host* #:*body* #:*headers* #:*cookies* #:request-with~% ~{#:~a~^~%~}))"
+ (:export ~%~{ #:~a~%~}))"
(app-client-package-name app)
(all-function-names app))
(terpri out)
@@ -188,9 +168,11 @@ COOKIES should be an instance of CL-COOKIE:COOKIE-JAR. Defaults to
(defun generate-client-system (directory app)
(assert (uiop:directory-exists-p directory))
+
(alexandria:write-string-into-file
(generate-defsystem-for-client-of-app app)
(merge-pathnames (client-asd-file-name app) directory))
+
(alexandria:write-string-into-file
(with-output-to-string (*standard-output*)
(princ (generate-defpackage-for-client-of-app app))