summaryrefslogtreecommitdiff
path: root/src/tg-types/macros.lisp
diff options
context:
space:
mode:
authorGravatar Uko Kokņevičs2025-10-10 12:30:46 +0300
committerGravatar Uko Kokņevičs2025-10-10 12:30:46 +0300
commitb4c1f66e1631f40d8a7d0f80523470677a91381f (patch)
treeef3aa478cfca05dc27e0777f9c42813d4268e54a /src/tg-types/macros.lisp
parentAdd a helper reply-message function (diff)
downloadukkoclot-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.lisp82
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))))))