 #_
 (defmacro define-dispatch-table [name & opts-and-entries]
   (def ~name `(compile-dispatch-table ~@opts-and-entries)))
+
+(defn default-pipeline [handler]
+  (-> handler
+    rook/wrap-with-function-arg-resolvers
+    sv/wrap-with-schema-validation))
+
+(defn namespace-dispatch-table
+  "Examines the given namespace and produces a dispatch table in a
+  format intelligible to compile-dispatch-table."
+  ([ns-sym]
+     (namespace-dispatch-table [] ns-sym))
+  ([context-pathvec ns-sym]
+     (namespace-dispatch-table context-pathvec ns-sym default-pipeline))
+  ([context-pathvec ns-sym pipeline]
+     (try
+       (require ns-sym)
+       (catch Exception e
+         (throw (ex-info "failed to require ns in namespace-dispatch-table"
+                  {:context-pathvec context-pathvec
+                   :ns              ns-sym
+                   :pipeline        pipeline}
+                  e))))
+     (let [context-pathvec ((fnil conj [])
+                            context-pathvec
+                            (peek (string/split (name ns-sym) #"\.")))]
+       [(->> ns-sym
+          ns-publics
+          (keep (fn [[k v]]
+                  (if (ifn? @v)
+                    (if-let [route-spec (or (:route-spec (meta v))
+                                            (get default-mappings k))]
+                      (conj route-spec (symbol (name ns-sym) (name k)))))))
+          (list* context-pathvec pipeline)
+          vec)])))

  (:require [clojure.core.match :refer [match]]
            [clojure.string :as string]
            [clojure.pprint :as pp]
            [io.aviso.rook.internals :as internals]))

(defn pprint-code [form]
  (pp/write form :dispatch pp/code-dispatch))


    [[method pathvec verb-fn pipeline]
     [method' (into pathvec pathvec') verb-fn' pipeline']
     ...].

  Entries may also take the alternative form of

    [pathvec pipeline? & entries],

  In which case pathvec and pipeline? (if present) will provide a
  context pathvec and a default pipeline for the nested entries
  without introducing a separate route."
  [dispatch-table]
  (letfn [(unnest-entry [default-pipeline [x :as entry]]
            (cond
              (keyword? x)
              (let [[method pathvec verb-fn pipeline & nested-table] entry]
                (if nested-table
                  (into [[method pathvec verb-fn
                          (or pipeline default-pipeline)]]
                    (unnest-table pathvec nested-table))
                  (cond-> [entry]
                    (nil? pipeline) (assoc-in [0 3] default-pipeline))))

              (vector? x)
              (let [[context-pathvec & maybe-pipeline+entries] entry
                    pipeline (if-not (vector? (first maybe-pipeline+entries))
                               (first maybe-pipeline+entries))
                    entries  (if pipeline
                               (next maybe-pipeline+entries)
                               maybe-pipeline+entries)]
                (unnest-table context-pathvec pipeline entries))))
          (unnest-table [context-pathvec default-pipeline entries]
            (mapv (fn [[_ pathvec :as unnested-entry]]
                    (assoc unnested-entry 1 (into context-pathvec pathvec)))
              (mapcat (partial unnest-entry default-pipeline) entries)))]
    (unnest-table [] nil dispatch-table)))

(defn keywords->symbols
  "Converts keywords in xs to symbols, leaving other items unchanged."
