diff options
| author | 2025-10-10 12:30:46 +0300 | |
|---|---|---|
| committer | 2025-10-10 12:30:46 +0300 | |
| commit | b4c1f66e1631f40d8a7d0f80523470677a91381f (patch) | |
| tree | ef3aa478cfca05dc27e0777f9c42813d4268e54a /src/tg-types/macros.lisp | |
| parent | Add a helper reply-message function (diff) | |
| download | ukkoclot-b4c1f66e1631f40d8a7d0f80523470677a91381f.tar.gz ukkoclot-b4c1f66e1631f40d8a7d0f80523470677a91381f.tar.xz ukkoclot-b4c1f66e1631f40d8a7d0f80523470677a91381f.zip | |
Bunch of changes
- Animations
- Rewrite of serialization deserialization
- Bunch of new TG types
Diffstat (limited to 'src/tg-types/macros.lisp')
| -rw-r--r-- | src/tg-types/macros.lisp | 82 |
1 files changed, 45 insertions, 37 deletions
diff --git a/src/tg-types/macros.lisp b/src/tg-types/macros.lisp index 668df17..a8a3d96 100644 --- a/src/tg-types/macros.lisp +++ b/src/tg-types/macros.lisp | |||
| @@ -2,26 +2,27 @@ | |||
| 2 | ;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com> | 2 | ;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com> |
| 3 | (defpackage :ukkoclot/tg-types/macros | 3 | (defpackage :ukkoclot/tg-types/macros |
| 4 | (:use :c2cl) | 4 | (:use :c2cl) |
| 5 | (:import-from :ukkoclot/bot/impl :arg-encode :bot :do-call :will-arg-encode) | 5 | (:import-from :ukkoclot/bot/impl :bot :do-call :parse-value) |
| 6 | (:import-from :ukkoclot/hash-tables :gethash-lazy) | 6 | (:import-from :ukkoclot/hash-tables :gethash-lazy) |
| 7 | (:import-from :ukkoclot/strings :lisp->snake-case) | 7 | (:import-from :ukkoclot/strings :lisp->snake-case) |
| 8 | (:local-nicknames | ||
| 9 | (:jzon :com.inuoe.jzon)) | ||
| 8 | (:export :define-tg-method :define-tg-type)) | 10 | (:export :define-tg-method :define-tg-type)) |
| 9 | (in-package :ukkoclot/tg-types/macros) | 11 | (in-package :ukkoclot/tg-types/macros) |
| 10 | 12 | ||
| 11 | (eval-when (:compile-toplevel :load-toplevel :execute) | 13 | (eval-when (:compile-toplevel :load-toplevel :execute) |
| 12 | (defstruct (field (:constructor make-field%)) name type default skip-if-default (parser 'identity)) | 14 | (defstruct (field (:constructor make-field%)) name type default skip-if-default) |
| 13 | 15 | ||
| 14 | (defparameter +unique+ (gensym)) | 16 | (defparameter +unique+ (gensym)) |
| 15 | 17 | ||
| 16 | (defun make-field (name type &optional (default +unique+) &key (parser 'identity) (skip-if-default (not (eq default +unique+)))) | 18 | (defun make-field (name type &optional (default +unique+) &key (skip-if-default (not (eq default +unique+)))) |
| 17 | (let ((default (if (eq default +unique+) | 19 | (let ((default (if (eq default +unique+) |
| 18 | (list 'error (format nil "No value given for ~A" name)) | 20 | (list 'error (format nil "No value given for ~A" name)) |
| 19 | default))) | 21 | default))) |
| 20 | (make-field% :name name | 22 | (make-field% :name name |
| 21 | :type type | 23 | :type type |
| 22 | :default default | 24 | :default default |
| 23 | :skip-if-default skip-if-default | 25 | :skip-if-default skip-if-default))) |
| 24 | :parser parser))) | ||
| 25 | 26 | ||
| 26 | (defun parse-field-specs (field-specs) | 27 | (defun parse-field-specs (field-specs) |
| 27 | (loop for field-spec in field-specs | 28 | (loop for field-spec in field-specs |
| @@ -33,6 +34,11 @@ | |||
| 33 | (defun field-accessor (struc-name field) | 34 | (defun field-accessor (struc-name field) |
| 34 | (intern (concatenate 'string (symbol-name struc-name) "-" (symbol-name (field-name field))))) | 35 | (intern (concatenate 'string (symbol-name struc-name) "-" (symbol-name (field-name field))))) |
| 35 | 36 | ||
| 37 | (defun field->coerced-field-spec (field struc-name obj-name) | ||
| 38 | `(list ,(string-downcase (lisp->snake-case (symbol-name (field-name field)))) | ||
| 39 | (,(field-accessor struc-name field) ,obj-name) | ||
| 40 | ',(field-type field))) | ||
| 41 | |||
| 36 | (defun field->defun-spec (field) | 42 | (defun field->defun-spec (field) |
| 37 | (list (field-name field) (field-default field))) | 43 | (list (field-name field) (field-default field))) |
| 38 | 44 | ||
| @@ -58,10 +64,8 @@ | |||
| 58 | `(setf (gethash ,hash-key ,hash-table-sym) (,(field-accessor name field) ,struc))))) | 64 | `(setf (gethash ,hash-key ,hash-table-sym) (,(field-accessor name field) ,struc))))) |
| 59 | 65 | ||
| 60 | (defun field->let-gethash-spec (field hash-table-sym) | 66 | (defun field->let-gethash-spec (field hash-table-sym) |
| 61 | (list (field-name field) | 67 | `(,(field-name field) |
| 62 | (list 'funcall | 68 | (parse-value ',(field-type field) ,(field->gethash-spec field hash-table-sym)))) |
| 63 | (list 'function (field-parser field)) | ||
| 64 | (field->gethash-spec field hash-table-sym)))) | ||
| 65 | 69 | ||
| 66 | (defun field->make-spec (field) | 70 | (defun field->make-spec (field) |
| 67 | (list (intern (symbol-name (field-name field)) :keyword) | 71 | (list (intern (symbol-name (field-name field)) :keyword) |
| @@ -72,35 +76,40 @@ | |||
| 72 | 76 | ||
| 73 | ;; TODO: Automatically derive path from name | 77 | ;; TODO: Automatically derive path from name |
| 74 | ;; TODO: Automatically derive mapfn from type | 78 | ;; TODO: Automatically derive mapfn from type |
| 75 | ;; TODO: Skip values that are already their defaults | ||
| 76 | (defmacro define-tg-method ( | 79 | (defmacro define-tg-method ( |
| 77 | (name type path mapfn &optional (method :POST)) | 80 | (name type path &optional (method :POST)) |
| 78 | &body field-specs) | 81 | &body field-specs) |
| 79 | (let ((fields (parse-field-specs field-specs)) | 82 | (let* ((fields (parse-field-specs field-specs)) |
| 80 | (args-plist (gensym "ARGS-PLIST-")) | 83 | (revfields (reverse fields)) |
| 81 | (bot (gensym "BOT-"))) | 84 | (args (gensym "ARGS")) |
| 85 | (bot (gensym "BOT-"))) | ||
| 82 | `(progn | 86 | `(progn |
| 83 | (declaim (ftype (function (bot &key ,@(loop for field in fields | 87 | (declaim (ftype (function (bot &key ,@(loop for field in fields |
| 84 | collect (field->ftype-spec field))) | 88 | collect (field->ftype-spec field))) |
| 85 | ,type) | 89 | ,type) |
| 86 | ,name)) | 90 | ,name)) |
| 87 | (defun ,name (,bot &rest ,args-plist &key ,@(loop for field in fields collect (field->defun-spec field))) | 91 | (defun ,name (,bot &key ,@(loop for field in fields collect (field->defun-spec field))) |
| 88 | (declare ,@(loop for field in fields collect (list 'ignore (field-name field)))) | 92 | (let (,args) |
| 89 | (do-call ,bot ,method ,path ,mapfn ,args-plist))))) | 93 | ,@(loop for field in revfields |
| 94 | collecting | ||
| 95 | (if (field-skip-if-default field) | ||
| 96 | `(unless (equal ,(field-name field) ,(field-default field)) | ||
| 97 | (setf ,args (acons ',(field-name field) ,(field-name field) ,args))) | ||
| 98 | `(setf ,args (acons ',(field-name field) ,(field-name field) ,args)))) | ||
| 99 | (do-call ,bot ,method ,path ',type ,args)))))) | ||
| 90 | 100 | ||
| 91 | (defmacro define-tg-type (name &body field-specs) | 101 | (defmacro define-tg-type (name &body field-specs) |
| 92 | (let* ((fields (parse-field-specs field-specs)) | 102 | (let* ((fields (parse-field-specs field-specs)) |
| 93 | (revfields (reverse fields)) | 103 | (revfields (reverse fields)) |
| 94 | (make-name (intern (concatenate 'string "MAKE-" (symbol-name name)))) | 104 | (make-name (intern (concatenate 'string "MAKE-" (symbol-name name)))) |
| 95 | (hash->name (intern (concatenate 'string "HASH->" (symbol-name name)))) | ||
| 96 | (parse-name-array (intern (concatenate 'string "PARSE-" (symbol-name name) "-ARRAY"))) | ||
| 97 | (printer (gensym (concatenate 'string "PRINT-" (symbol-name name)))) | 105 | (printer (gensym (concatenate 'string "PRINT-" (symbol-name name)))) |
| 98 | (hash (gensym "HASH-")) | 106 | (hash (gensym "HASH-")) |
| 99 | (array (gensym "ARRAY-")) | ||
| 100 | (struc (gensym (symbol-name name))) | 107 | (struc (gensym (symbol-name name))) |
| 101 | (stream (gensym "STREAM")) | 108 | (stream (gensym "STREAM")) |
| 102 | (depth (gensym "DEPTH")) | 109 | (depth (gensym "DEPTH")) |
| 103 | (pprint-args (gensym "PPRINT-ARGS"))) | 110 | (pprint-args (gensym "PPRINT-ARGS")) |
| 111 | (res (gensym "RES")) | ||
| 112 | (type (gensym "TYPE"))) | ||
| 104 | `(progn | 113 | `(progn |
| 105 | (defstruct (,name (:print-function ,printer)) | 114 | (defstruct (,name (:print-function ,printer)) |
| 106 | ,@(loop for field in fields | 115 | ,@(loop for field in fields |
| @@ -116,19 +125,18 @@ | |||
| 116 | (setf ,pprint-args (list* ',(field-name field) value ,pprint-args)))) | 125 | (setf ,pprint-args (list* ',(field-name field) value ,pprint-args)))) |
| 117 | `(setf ,pprint-args (list* ',(field-name field) (,(field-accessor name field) ,struc) ,pprint-args)))) | 126 | `(setf ,pprint-args (list* ',(field-name field) (,(field-accessor name field) ,struc) ,pprint-args)))) |
| 118 | (format ,stream "~A~<[~;~@{~_~1I~W = ~W~^, ~}~;]~:>" ',name ,pprint-args))) | 127 | (format ,stream "~A~<[~;~@{~_~1I~W = ~W~^, ~}~;]~:>" ',name ,pprint-args))) |
| 119 | (defun ,hash->name (,hash) | 128 | (defmethod parse-value ((,type (eql ',name)) ,hash) |
| 120 | (when ,hash | 129 | (let ,(loop for field in fields |
| 121 | (let ,(loop for field in fields | 130 | collect (field->let-gethash-spec field hash)) |
| 122 | collect (field->let-gethash-spec field hash)) | 131 | (,make-name ,@(loop for field in fields |
| 123 | (,make-name ,@(loop for field in fields | 132 | append (field->make-spec field))))) |
| 124 | append (field->make-spec field)))))) | 133 | (defmethod jzon:coerced-fields ((,struc ,name)) |
| 125 | (defmethod arg-encode ((,struc ,name)) | 134 | (let (,res) |
| 126 | (let ((,hash (make-hash-table))) | 135 | ,@(loop for field in revfields |
| 127 | ,@(loop for field in fields | 136 | collecting |
| 128 | collect (field->sethash-spec field name struc hash)) | 137 | (if (field-skip-if-default field) |
| 129 | ,hash)) | 138 | `(let ((value (,(field-accessor name field) ,struc))) |
| 130 | (defmethod will-arg-encode ((,struc ,name)) | 139 | (unless (equal value ,(field-default field)) |
| 131 | t) | 140 | (setf ,res (cons ,(field->coerced-field-spec field name struc) ,res)))) |
| 132 | (defun ,parse-name-array (,array) | 141 | `(setf ,res (cons ,(field->coerced-field-spec field name struc) ,res)))) |
| 133 | (when ,array | 142 | ,res))))) |
| 134 | (map 'vector #',hash->name ,array)))))) | ||