|
| 1 | +(in-package #:quicklisp-controller) |
| 2 | + |
| 3 | +(defvar *provenance-context* nil |
| 4 | + "This variable is used to store provenance updates in progress.") |
| 5 | + |
| 6 | +(defun serializable-provenance-object-p (object) |
| 7 | + (or (and (consp object) |
| 8 | + (every #'serializable-provenance-object-p object)) |
| 9 | + (typep object '(or keyword string integer boolean)))) |
| 10 | + |
| 11 | +(defun utf8-decode (octets) |
| 12 | + (read-from-string (babel:octets-to-string octets :encoding :utf-8))) |
| 13 | + |
| 14 | +(defun utf8-encode (object) |
| 15 | + (unless (serializable-provenance-object-p object) |
| 16 | + (error "Cannot serialize ~S" object)) |
| 17 | + (babel:string-to-octets (prin1-to-string object) :encoding :utf-8)) |
| 18 | + |
| 19 | +(defun load-provenance-db (db-file) |
| 20 | + (let ((table (make-hash-table :test 'equal))) |
| 21 | + (when (probe-file db-file) |
| 22 | + (zcdb:map-cdb |
| 23 | + (lambda (raw-key raw-value) |
| 24 | + (let ((key (utf8-decode raw-key)) |
| 25 | + (value (utf8-decode raw-value))) |
| 26 | + (setf (gethash key table) value))) |
| 27 | + db-file)) |
| 28 | + table)) |
| 29 | + |
| 30 | +(defun save-provenance-db (table db-file) |
| 31 | + (zcdb:with-output-to-cdb (cdb db-file (make-pathname :type "cdb-tmp" |
| 32 | + :defaults db-file)) |
| 33 | + (maphash (lambda (key value) |
| 34 | + (zcdb:add-record (utf8-encode key) |
| 35 | + (utf8-encode value) |
| 36 | + cdb)) |
| 37 | + table))) |
| 38 | + |
| 39 | +(defun call-with-provenance-context (db-file fun) |
| 40 | + (setf db-file (merge-pathnames db-file)) |
| 41 | + (if *provenance-context* |
| 42 | + (if (equalp db-file (car *provenance-context*)) |
| 43 | + (funcall fun) |
| 44 | + (error "Nested provenance context - ~S vs ~S" |
| 45 | + db-file |
| 46 | + (car *provenance-context*))) |
| 47 | + (let ((*provenance-context* (cons db-file |
| 48 | + (load-provenance-db db-file)))) |
| 49 | + (multiple-value-prog1 |
| 50 | + (funcall fun) |
| 51 | + (save-provenance-db (cdr *provenance-context*) db-file))))) |
| 52 | + |
| 53 | +(defmacro with-provenance-context ((db-file) &body body) |
| 54 | + `(call-with-provenance-context ,db-file (lambda () ,@body))) |
| 55 | + |
| 56 | +(defun ensure-provenance-context () |
| 57 | + (unless *provenance-context* |
| 58 | + (error "No provenance context is in effect!")) |
| 59 | + (cdr *provenance-context*)) |
| 60 | + |
| 61 | +(defun save-provenance (project key &rest plist &key &allow-other-keys) |
| 62 | + (let ((table (ensure-provenance-context))) |
| 63 | + (push (list key plist) (gethash project table)) |
| 64 | + plist)) |
| 65 | + |
0 commit comments