git.fiddlerwoaroof.com
objc-data-extractors.lisp
d5a8a26b
 ;; objc-data-extractor.lisp
 
 
 ;; [[file:~/git_repos/objc-lisp-bridge/README.org::*objc-data-extractor.lisp][objc-data-extractor.lisp:1]]
47822ccc
 (defpackage :objc-runtime.data-extractors
   (:use :cl )
   (:export
    #:extract-from-objc
    #:define-extractor
    #:clear-extractors
    #:add-extractor
382ab041
    #:get-plist
    #:objc-typecase))
47822ccc
 
1aa0f8f7
 (in-package :objc-runtime.data-extractors)
 (named-readtables:in-readtable :objc-readtable)
 
 (defun get-plist (file)
   [#@NSDictionary @(dictionaryWithContentsOfFile:)
                   :pointer (objc-runtime::make-nsstring file)])
 
 (defun objc-subclass-p (sub super)
   (unless (or (cffi:null-pointer-p sub)
               (cffi:null-pointer-p super))
     (or (eql sub super)
         (= [sub @(isSubclassOfClass:) :pointer [super @(class)]]#
            1))))
 
 (defun order-objc-classes (classes &rest r &key key)
   (declare (ignore key))
   (apply 'stable-sort
          (copy-seq classes)
          'objc-subclass-p
          r))
 
 (defun objc-isa (obj class)
   (unless (or (cffi:null-pointer-p obj)
               (cffi:null-pointer-p class))
     (= [obj @(isKindOfClass:) :pointer class]#
        1)))
 
 (defun objc-pick-by-type (obj pairs)
   (assoc obj
          (order-objc-classes pairs :key 'car)
          :test 'objc-isa))
 
 (serapeum:eval-always
   (defun make-cases (cases obj)
     (mapcar (serapeum:op
               `(if (objc-isa ,obj ,(car _1))
                    (progn ,@(cdr _1))))
47822ccc
                    cases)))
1aa0f8f7
 
 (defmacro objc-typecase (form &body ((case-type &body case-handler) &rest cases))
   (alexandria:once-only (form)
     (let* ((initial-cases `((,case-type ,@case-handler) ,@(butlast cases)))
            (cases (fw.lu:rollup-list (make-cases initial-cases form)
                                      (if (eql t (caar (last cases)))
                                          `((progn ,@(cdar (last cases))))
                                          (make-cases (last cases) form)))))
       cases)))
 
 (defun map-nsarray (fn arr)
   (unless (and (cffi:pointerp arr)
                (objc-isa arr #@NSArray))
     (error "must provide a NSArray pointer"))
   (loop for x below [arr @(count)]#
      collect (funcall fn [arr @(objectAtIndex:) :int x])))
 
 (defun nsarray-contents (arr)
   (unless (and (cffi:pointerp arr)
                (objc-isa arr #@NSArray))
     (error "must provide a NSArray pointer"))
   (dotimes (n [arr @(count)]#)
     (let ((obj [arr @(objectAtIndex:) :int n ]))
       (objc-typecase obj
         (#@NSString (format t "~&string~%"))
         (#@NSArray (format t "~&array~%"))
         (#@NSDictionary (format t "~&dictionary~%"))
         (t (format t "~&other... ~s~%" (objc-runtime::objc-class-get-name
                                         (objc-runtime::object-get-class obj))))))))
 
 (defmacro funcall-some (fun &rest args)
   (alexandria:once-only (fun)
     `(if ,fun
          (funcall ,fun ,@args))))
 
47822ccc
 (defvar *objc-extractors* (list)
   "Functions called to extract specific data types")
 
1aa0f8f7
 (defun extract-from-objc (obj)
   (objc-typecase obj
     (#@NSDate [[[[#@NSISO8601DateFormatter @(alloc)]
                  @(init)]
                 @(stringFromDate:) :pointer obj]
                @(UTF8String)]s)
     (#@NSString [obj @(UTF8String)]s)
     (#@NSNumber (parse-number:parse-number
                  (objc-runtime::extract-nsstring
                   [obj @(stringValue)])))
     (#@NSArray (map-nsarray #'extract-from-objc obj))
     (#@NSDictionary (fw.lu:alist-string-hash-table
                      (pairlis (map-nsarray #'extract-from-objc [obj @(allKeys)])
                               (map-nsarray #'extract-from-objc [obj @(allValues)]))))
     (t (or (funcall-some (cdr (objc-pick-by-type obj *objc-extractors*))
                          obj)
            obj))))
47822ccc
 
 (defmacro define-extractor (class (o) &body body)
   `(serapeum:eval-always
      (add-extractor ,class
                     (lambda (,o)
                       ,@body))
      *objc-extractors*))
 
 (defun clear-extractors ()
   (setf *objc-extractors* ()))
 
 (serapeum:eval-always
   (defun add-extractor (class cb)
     (unless (member class *objc-extractors* :test 'cffi:pointer-eq :key #'car)
       (setf *objc-extractors*
             (merge 'list *objc-extractors* (list (cons class cb))
                    'objc-subclass-p
                    :key 'car)))
     *objc-extractors*))
d5a8a26b
 ;; objc-data-extractor.lisp:1 ends here