summaryrefslogtreecommitdiff
path: root/src/bot/method-macros.lisp
blob: 0500de9aea30777e566f8de657341be31990688a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
;; SPDX-License-Identifier: EUPL-1.2
;; SPDX-FileCopyrightText: 2025 Uko Kokņevičs <perkontevs@gmail.com>
(defpackage :ukkoclot/bot/method-macros
  (:use :c2cl :iterate)
  (:import-from :ukkoclot/state :bot)
  (:import-from :ukkoclot/transport :do-call)
  (:export :define-tg-method))
(in-package :ukkoclot/bot/method-macros)

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defstruct (param (:constructor make-param%)) name type default skip-if-default)

  (defparameter +unique+ (gensym))

  (defun make-param (name type &optional (default +unique+) &key (skip-if-default (not (eq default +unique+))))
    (let ((default (if (eq default +unique+)
                       `(error ,(format nil "No value given for ~A" name))
                       default)))
      (make-param% :name name
                   :type type
                   :default default
                   :skip-if-default skip-if-default)))

  (defun parse-param-specs (param-specs)
    (iter (for param-spec in param-specs)
          (collect (apply #'make-param param-spec))))

  (defun emit-append-to-args (param args)
    `(setf ,args (acons ',(param-name param) ,(param-name param) ,args)))

  (defun emit-arg-type (param)
    `(,(intern (symbol-name (param-name param)) :keyword)
      ,(param-type param)))

  (defun emit-defun-arg (param)
    `(,(param-name param) ,(param-default param)))

  (defun emit-defun (name return-type params method path)
    (let ((revparams (reverse params))
          (args (gensym "ARGS"))
          (bot (gensym "BOT")))
      `(defun ,name (,bot &key ,@(iter (for param in params)
                                       (collect (emit-defun-arg param))))
         (let (,args)
           ,@(iter (for param in revparams)
                   (collect (if (param-skip-if-default param)
                                `(unless (equal ,(param-name param)
                                                ,(param-default param))
                                   ,(emit-append-to-args param args))
                                (emit-append-to-args param args))))
           (do-call ,bot ,method ,path ',return-type ,args)))))

  (defun emit-ftype (name return-type params)
    `(declaim (ftype (function (bot &key ,@(iter (for param in params)
                                                 (collect (emit-arg-type param))))
                               ,return-type)
                     ,name))))

;; TODO: Automatically derive path from name
(defmacro define-tg-method (
                            (name type path &optional (method :POST))
                            &body param-specs)
  (let ((params (parse-param-specs param-specs)))
    `(progn
       ,(emit-ftype name type params)
       ,(emit-defun name type params method path))))