git.fiddlerwoaroof.com
data-class.lisp
93a579ec
 (in-package #:data-class)
 
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (defun get-channel-element (xml-dom el)
     ($ (inline xml-dom) el (text) (node))))
 
 (defun element-name-from-symbol (sym)
   (let* ((base (string-downcase sym))
          (split (split-sequence:split-sequence #\~ base))
          (capit (cons (car split) (map 'list #'nstring-capitalize (cdr split)))))
     (apply #'concatenate 'string capit)))
 
 (defmacro ensure-slot (sym &body or-else)
   `(handler-case
      ,sym
      (unbound-slot (c)
        (declare (ignore c))
        ,@or-else)))
 
 (defmacro lazy-load-slot (class-name doc-slot root-el name tag-name &key transform value)
   `(defmethod ,name :before ((self ,class-name))
      (with-slots (,name ,doc-slot) self
        (ensure-slot ,name
          (alet ,(or value
                     `(get-channel-element ,doc-slot
                                           ,(format nil "~a > ~a" root-el tag-name)))
            ,(if transform
               `(setf ,name (when it (,transform it)))
               `(setf ,name it)))))))
 
58a15352
 (defgeneric %all-slots (self format))
 (defun all-slots (self &optional format)
   (%all-slots self format))
 
 (defgeneric slot-tags (self))
 
 (defun process-slots-for-data-class (slots)
5dc30073
   (mapcar (fw.lu::destructuring-lambda ((slot tag . rest))
             (let ((tag (etypecase tag
                          (symbol (string-downcase tag))
                          (string tag))))
               (list* slot (make-keyword slot) tag rest)))
           (fw.lu:ensure-mapping slots)))
58a15352
 
 (deftest process-slots-for-data-class ()
   (let ((tc-1 '(a))
         (tc-2 '((a b)))
         (tc-3 '((a "b")))
         (tc-4 '((a b c)))
         (tc-5 '((a "b" c)))
         (tc-6 '((a "bC")))
         (tc-7 '((a "bC" d))))
     (should be equal '((a :a "a")) (process-slots-for-data-class tc-1))
     (should be equal '((a :a "b")) (process-slots-for-data-class tc-2))
     (should be equal '((a :a "b")) (process-slots-for-data-class tc-3))
     (should be equal '((a :a "b" c)) (process-slots-for-data-class tc-4))
     (should be equal '((a :a "b" c)) (process-slots-for-data-class tc-5))
     (should be equal '((a :a "bC")) (process-slots-for-data-class tc-6))
     (should be equal '((a :a "bC" d)) (process-slots-for-data-class tc-7))))
93a579ec
 
 (defmacro define-data-class (name (doc-slot root-el) (&rest superclasses) &body slots)
58a15352
   (declare (optimize (debug 3)))
   (flet ((make-slot-spec (slot slot-keyword)
            `(,slot :initarg ,slot-keyword :accessor ,slot)))
     (let ((slots (process-slots-for-data-class slots)))
       `(progn
          (defclass ,name ,superclasses
            ((slot-tags :allocation :class :initform
                        ',(loop for (_ slot-keyword tag) in slots
                                collect (cons slot-keyword tag)))
             ,@(mapcar (fw.lu::destructuring-lambda ((slot slot-keyword . r))
                         (declare (ignore r))
                         (make-slot-spec slot slot-keyword))
                       slots)))
          ,@(loop for (slot _ tag-name . rest) in slots
                  collect `(lazy-load-slot ,name ,doc-slot ,root-el ,slot ,tag-name ,@rest))
          (defmethod %all-slots ((self ,name) format)
aafdf70e
            (pairlis (list ,@(mapcar (fw.lu::alambda (cadr fw.lu::it)) slots))
58a15352
                     (list ,@(loop for (slot) in slots collect `(,slot self)))))))))