git.fiddlerwoaroof.com
bundle-utils.lisp
d0b34703
 (in-package :objc-runtime.bundle-utils)
 (named-readtables:in-readtable :objc-readtable)
 
 (defun bundle-resource-root ()
   (uiop:ensure-directory-pathname
    [[[#@NSBundle @(mainBundle)] @(resourceURL)] @(fileSystemRepresentation)]s))
 
 (defun application-support-directory (&optional (scope :user))
   (let ((next-step (make-pathname :directory (list :relative (objc-runtime.data-extractors:extract-from-objc
                                                               [[[#@NSBundle @(mainBundle)]
                                                                 @(infoDictionary)]
                                                                @(objectForKey:)
                                                                :pointer @"CFBundleIdentifier"])))))
     (car
      (mapcan (alexandria:compose 'serapeum:unsplice
                                  (lambda (p) (when p (merge-pathnames next-step p)))
                                  'probe-file)
              (mapcar (lambda (it) [it @(fileSystemRepresentation)]?s)
                      (objc-runtime.data-extractors:extract-from-objc
                       [[#@NSFileManager @(defaultManager)] @(URLsForDirectory:inDomains:)
                        :int 14 ;; NSApplicationSupportDirectory
                        :int (ccase scope
                               (:user 1)
                               (:local 2)
                               (:network 4))]))))))
 
 (defun setup-bundle-logical-pathnames ()
   (setf (logical-pathname-translations "BUNDLE")
         `(("BUNDLE:RESOURCES;**;*.*.*" ,(bundle-resource-root))
           ("BUNDLE:SUPPORT;USER;**;*.*.*" ,(application-support-directory :user))
           ("BUNDLE:SUPPORT;LOCAL;**;*.*.*" ,(application-support-directory :local)))))
 
 (defun ensure-application-support ()
   (setup-bundle-logical-pathnames)
   (translate-logical-pathname
    (ensure-directories-exist
     #P"BUNDLE:APPLICATION-SUPPORT;USER;")))
 
 (named-readtables:defreadtable config
   (:case :preserve)
   (:syntax-from :standard #\) #\))
   (:macro-char #\( (lambda (s c)
                      c
                      (read-delimited-list #\) s t))
                nil)
   (:macro-char #\, (lambda (s c)
                      c
                      (values))
                nil)
   (:syntax-from :standard #\" #\")
   (:syntax-from :standard #\: #\:)
   (:syntax-from :standard #\) #\})
   (:macro-char #\{ (lambda (s c)
                      c
                      (alexandria:plist-hash-table (read-delimited-list #\} s t)
                                                   :test 'equal))
                nil)
   (:syntax-from :standard #\) #\])
   (:macro-char #\[ (lambda (s c)
                      c
                      (apply #'vector (read-delimited-list #\] s t)))
                nil))
 
 (defparameter *config-pprint*
   (copy-pprint-dispatch))
 
 (set-pprint-dispatch 'hash-table
                      (lambda (s hash-table)
                        (pprint-logical-block (s nil)
                          (princ "{"  s)
                          (let ((v (fset:convert 'list (fset:convert 'fset:map hash-table))))
                            (when v
                              (pprint-logical-block (s v)
                                (pprint-indent :block 0 s)
                                (loop do
                                  (destructuring-bind (key . value) (pprint-pop)
                                    (format s "~s ~s" key value)
                                    (pprint-exit-if-list-exhausted)
                                    (princ ", " s)
                                    (pprint-newline :linear s))))))
                          (princ #\} s)))
                      1 *config-pprint*)
 
 (set-pprint-dispatch 'vector
                      (lambda (s vector)
                        (pprint-logical-block (s nil)
                          (princ "["  s)
                          (let ((v (coerce vector 'list)))
                            (when v
                              (pprint-logical-block (s v)
                                (pprint-indent :block 0 s)
                                (loop do
                                  (prin1 (pprint-pop) s)
                                  (pprint-exit-if-list-exhausted)
                                  (princ ", " s)
                                  (pprint-newline :linear s)))))
                          (princ #\] s)))
                      1 *config-pprint*)
 
 (defun print-for-config (object s)
   (let ((*print-readably* t)
         (*print-pprint-dispatch* *config-pprint*))
     (pprint object s)))
 
 (defun read-from-config (s)
   (let ((*readtable* (named-readtables:find-readtable 'config)))
     (read s)))