From c201a822f264041a1b9169824c0f9acbfae9cf6e Mon Sep 17 00:00:00 2001 From: colin Date: Sat, 18 Nov 2023 15:25:51 -0800 Subject: version 1.0 --- src/client/dexador.lisp | 205 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 src/client/dexador.lisp (limited to 'src/client/dexador.lisp') diff --git a/src/client/dexador.lisp b/src/client/dexador.lisp new file mode 100644 index 0000000..2503b5e --- /dev/null +++ b/src/client/dexador.lisp @@ -0,0 +1,205 @@ +;;;; lazybones-client.lisp -- macro to generate a set of http request functions given an APP instance + +;; Copyright (C) 2022 Colin Okay + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + + +(defpackage #:lazybones/client.dexador + (:use #:cl) + (:local-nicknames (#:a #:alexandria-2)) + (:export #:generate)) + +(in-package :lazybones/client.dexador) + + + +(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 (string-downcase term)) + when (listp term) + do (princ (string-downcase (car term))) + when (and more (plusp (length term))) + 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." + (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-accepts-body-p (ep) + (member (lazybones::endpoint-method ep) '(:post :put :patch)) ) + +(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 + "(~{~a ~} %host &key ~:[~;%content-type %body ~] %headers %cookies)" + (append + (endpoint-defun-route-var-names ep) + (endpoint-defun-query-var-names ep)) + (endpoint-accepts-body-p 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." + (with-output-to-string (*standard-output*) + (loop + for first = t then nil + for varname in (endpoint-defun-query-var-names ep) + do + (princ "~@[") + (unless first (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) + "\" " + "%host " + (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~% ~a~%~{ ~a~^~%~})" + (string-downcase (symbol-name (lazybones::endpoint-method ep))) + (endpoint-defun-dexador-request-uri app ep) + (append + (if (endpoint-accepts-body-p ep) + (list ":content %body" + ":cookie-jar %cookies" + ":headers (if %content-type (cons (cons \"Content-Type\" %content-type) %headers) %headers)") + (list ":cookie-jar %cookies" + ":headers %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))) + + +(defun all-function-names (app) + (mapcar 'endpoint-defun-name (lazybones::app-endpoints app))) + +(defun app-client-package-name (app) + (format nil "~a-CLIENT" (lazybones::app-name app))) + + +(defun generate-defsystem-for-client-of-app (app) + (with-output-to-string (*standard-output*) + (princ "(asdf:defsystem #:") (princ (app-client-package-name app)) + (terpri) + (princ " :depends-on (#:dexador)") + (terpri) + (princ " :components ((:file ") + (princ #\") + (princ (string-downcase (app-client-package-name app))) + (princ #\") + (princ ")))"))) + + +(defun generate-defpackage-for-client-of-app (app) + (with-output-to-string (out) + (format + out + " +;;;; DO NOT EDIT! THIS FILE HAS BEEN GENERATED BY LAZYBONES-CLIENT + +(defpackage #:~a + (:use :cl) + (:export ~%~{ #:~a~^~%~}))" + (app-client-package-name app) + (all-function-names app)) + (terpri out) + (format out "(in-package :~a)" (app-client-package-name app)) + (terpri out))) + +(defun client-asd-file-name (app) + (format nil "~a.asd" (string-downcase (app-client-package-name app)))) + +(defun client-lisp-file-name (app) + (format nil "~a.lisp" (string-downcase (app-client-package-name app)))) + +(defun generate-client-functions-for-app (app) + (loop for ep in (lazybones::app-endpoints app) + collect (generate-defun-for-endpoint app ep))) + +(defun generate (directory app) + "Generate " + (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)) + (princ #\newline) (princ #\newline) + (princ #\newline) (princ #\newline) + (dolist (defun-string (generate-client-functions-for-app app)) + (princ defun-string) + (princ #\newline) (princ #\newline))) + (merge-pathnames (client-lisp-file-name app) directory)) + :ok) -- cgit v1.2.3