; your code goes here
(defparameter *config* (make-hash-table))
(define-condition value-not-found (error)
((text :initarg :text :reader text :initform "")))
(defmethod print-object ((obj value-not-found) stream)
(print-unreadable-object (obj stream :type t)
(format stream "~s" (slot-value obj 'text))))
(defun get-single-value (name)
(multiple-value-bind (v exist) (gethash name *config*)
(if exist v (error 'value-not-found :text (format nil "no config value for ~a" name)))))
(defun select-any (names)
(loop for name in names
do (let ((value (handler-case (get-single-value name)
(value-not-found () nil))))
(when value
(return-from select-any value))))
(error 'value-not-found :text (format nil "no config values in ANY ~a" names)))
(defun select-all (names)
(loop for name in names
collect (handler-case (get-single-value name)
(value-not-found ()
(error 'value-not-found
:text (format nil "no config value for ~a in ALL ~a" name names))))))
(defun select-values (spec)
(if (= (length spec) 1)
(get-single-value (car spec))
(case (car spec)
((:any) (select-any (cdr spec)))
((:all) (cons 'list (select-all (cdr spec))))
(t (error 'config-spec-unsupported "spec ~a unsupported" spec)))))
(defmacro get-value (&rest body)
(select-values body))
(defun set-value (name value)
(setf (gethash name *config*) value))
(set-value 'shard 5)
(set-value 'host "super-host.com")
(defun test ()
(get-value :any shard host)
(get-value :any host shard)
(get-value :all host shard)
(get-value :any shard unknown-value)
(get-value :all host unknown-value))