;;;; lazybones-documentation.lisp -- documenting APP instances (in-package :lazybones) (defun sorted-endpoints (endpoints) (sort (copy-seq endpoints) #'string< :key #'endpoint-route)) (defun generate-app-documentation (app) "For now, generates a single Markdown string that documents each endpoint in APP." (symbol-macrolet ((newline (progn (princ #\newline)(princ #\newline)))) (with-slots (title version endpoints (default-authorizer authorizer) default-content-type description) app (with-output-to-string (*standard-output*) (princ "# ") (princ title) (princ " - ") (princ "v") (princ version) newline (princ description) newline (princ "## Endpoints") (dolist (ep (sorted-endpoints endpoints)) (with-slots (method content-type route authorizer params endpoint-documentation) ep newline (princ "### ") (princ method) (princ " ") (princ (make-route-presentable route)) (terpri) (princ "*") (princ (if content-type content-type default-content-type )) (princ "*") newline (a:when-let (vars (endpoint-route-vars ep)) (princ "Route Variables: ") newline (dolist (var vars) (princ "- ") (princ var) (a:when-let (val-parser (route-var-value-parser ep var)) (princ ": ") (princ (strip-newlines (documentation val-parser 'function)))) (princ #\newline))) (when params newline (princ "Documented Query Parameters: ") newline (loop for (var parser) in params do (princ "- ") (princ (string-downcase (symbol-name var))) (princ ": ") (princ (strip-newlines (documentation parser 'function))) (princ #\newline))) (when authorizer (princ "Authorization Required: ") newline (cond ((function-or-function-name-p authorizer) (princ (ensure-blockquote (documentation authorizer 'function)))) ((function-or-function-name-p default-authorizer) (princ (ensure-blockquote (documentation default-authorizer 'function))))) newline) newline (princ endpoint-documentation) )))))) (defun ensure-blockquote (string) (concatenate 'string "> " (str:replace-all '(#\newline) " > " string))) (defun strip-newlines (string) (str:replace-all '(#\newline) "" string)) (defun function-or-function-name-p (thing) (or (functionp thing) (and (symbolp thing) (fboundp thing)))) (defun endpoint-route-vars (ep) "return a list of route variables for endpoint EP" (mapcar 'first (remove-if-not #'consp (endpoint-dispatch-pattern ep)))) (defun route-var-value-parser (ep var) (second (assoc var (remove-if-not #'consp (endpoint-dispatch-pattern ep))))) (defun make-route-presentable (routestring) (ppcre:regex-replace-all " [a-z0-9A-Z\-]+:" routestring ":"))